diff options
author | Robert Thurlow <Robert.Thurlow@Sun.COM> | 2009-12-09 17:27:22 -0600 |
---|---|---|
committer | Robert Thurlow <Robert.Thurlow@Sun.COM> | 2009-12-09 17:27:22 -0600 |
commit | 2f172c55ef76964744bc62b4500ece87f3089b4d (patch) | |
tree | 68a197e4eb4d77acf9993e2e3d75c3f3b06f6a86 /usr/src | |
parent | 1dbbbf767041f5cea7771826e2efc21c03bbffda (diff) | |
download | illumos-gate-2f172c55ef76964744bc62b4500ece87f3089b4d.tar.gz |
6232737 Client should support NFS4ERR_MOVED and fs_locations
6232743 Server should support NFS4ERR_MOVED and fs_locations
6891289 client panick mutex_vector_tryenter with some stress testing
Diffstat (limited to 'usr/src')
65 files changed, 5490 insertions, 1155 deletions
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs4.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs4.c index dc675cf905..bb1db5c979 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs4.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs4.c @@ -19,13 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - - #include <ctype.h> #include <string.h> #include <strings.h> @@ -578,7 +575,7 @@ static void showxdr_utf8string(char *); static char *utf8localize(utf8string *); static void utf8free(void); static void sum_pathname4(char *, size_t, pathname4 *); -static void detail_pathname4(pathname4 *pathp); +static void detail_pathname4(pathname4 *pathp, char *); static void sum_compname4(char *buf, size_t buflen, component4 *comp); static void detail_compname4(component4 *comp); @@ -696,8 +693,8 @@ interpret_nfs4_cb(int flags, int type, int xid, int vers, int proc, if (type == CALL) { (void) sprintf(line, "NFS C %s", - proc == CB_COMPOUND ? "CB4" : - cb_procnames_short[proc]); + proc == CB_COMPOUND ? "CB4" : + cb_procnames_short[proc]); line += strlen(line); if (proc == CB_COMPOUND) { @@ -706,15 +703,15 @@ interpret_nfs4_cb(int flags, int type, int xid, int vers, int proc, if (!xdr_utf8string(&xdrm, &tag)) longjmp(xdr_err, 1); sprintf(line, " (%.20s) %s", - utf8localize(&tag), - sum_cb_compound4args()); + utf8localize(&tag), + sum_cb_compound4args()); xdr_free(xdr_utf8string, (char *)&tag); } check_retransmit(line, xid); } else { (void) sprintf(line, "NFS R %s ", - proc == CB_COMPOUND ? "CB4" : - cb_procnames_short[proc]); + proc == CB_COMPOUND ? "CB4" : + cb_procnames_short[proc]); line += strlen(line); if (proc == CB_COMPOUND) sum_comp4res(line, sum_cb_compound4res); @@ -725,7 +722,7 @@ interpret_nfs4_cb(int flags, int type, int xid, int vers, int proc, show_header("NFS: ", "Sun NFS4 CallBack", len); show_space(); (void) sprintf(get_line(0, 0), "Proc = %d (%s)", - proc, cb_procnames_long[proc]); + proc, cb_procnames_long[proc]); if (proc == CB_COMPOUND) { if (type == CALL) { showxdr_utf8string("Tag = %s"); @@ -736,7 +733,7 @@ interpret_nfs4_cb(int flags, int type, int xid, int vers, int proc, status = getxdr_long(); showxdr_utf8string("Tag = %s"); sprintf(get_line(0, 0), "Status = %d (%s)", - status, status_name(status)); + status, status_name(status)); detail_cb_resop4(); } } @@ -766,8 +763,8 @@ interpret_nfs4(int flags, int type, int xid, int vers, int proc, if (type == CALL) { (void) sprintf(line, "NFS C %s", - proc == NFSPROC4_COMPOUND ? "4" : - procnames_short[proc]); + proc == NFSPROC4_COMPOUND ? "4" : + procnames_short[proc]); line += strlen(line); if (proc == NFSPROC4_COMPOUND) { @@ -776,15 +773,15 @@ interpret_nfs4(int flags, int type, int xid, int vers, int proc, if (!xdr_utf8string(&xdrm, &tag)) longjmp(xdr_err, 1); sprintf(line, " (%.20s) %s", - utf8localize(&tag), - sum_compound4args()); + utf8localize(&tag), + sum_compound4args()); xdr_free(xdr_utf8string, (char *)&tag); } check_retransmit(line, xid); } else { (void) sprintf(line, "NFS R %s ", - proc == NFSPROC4_COMPOUND ? "4" : - procnames_short[proc]); + proc == NFSPROC4_COMPOUND ? "4" : + procnames_short[proc]); line += strlen(line); if (proc == NFSPROC4_COMPOUND) @@ -796,7 +793,7 @@ interpret_nfs4(int flags, int type, int xid, int vers, int proc, show_header("NFS: ", "Sun NFS", len); show_space(); (void) sprintf(get_line(0, 0), "Proc = %d (%s)", - proc, procnames_long[proc]); + proc, procnames_long[proc]); if (proc == NFSPROC4_COMPOUND) { if (type == CALL) { showxdr_utf8string("Tag = %s"); @@ -807,7 +804,7 @@ interpret_nfs4(int flags, int type, int xid, int vers, int proc, status = getxdr_long(); showxdr_utf8string("Tag = %s"); sprintf(get_line(0, 0), "Status = %d (%s)", - status, status_name(status)); + status, status_name(status)); detail_nfs_resop4(); } } @@ -840,7 +837,7 @@ sum_compound4args(void) if (setjmp(xdr_err)) { bp = buf + strlen(buf); snprintf(bp, buflen - (bp - buf), - nfs4_fragged_rpc ? nfs4err_fragrpc : nfs4err_xdrfrag); + nfs4_fragged_rpc ? nfs4err_fragrpc : nfs4err_xdrfrag); return (buf); } @@ -863,7 +860,7 @@ sum_compound4args(void) longjmp(xdr_err, 1); } snprintf(bp, buflen - (bp - buf), "%s ", - opcode_name(one_op.argop)); + opcode_name(one_op.argop)); bp += strlen(bp); operand = sum_operand(&one_op); @@ -952,7 +949,7 @@ sum_cb_compound4args(void) if (setjmp(xdr_err)) { bp = buf + strlen(buf); snprintf(bp, buflen - (bp - buf), "<XDR Error or Fragmented" - " RPC>"); + " RPC>"); return (buf); } @@ -981,7 +978,7 @@ sum_cb_compound4args(void) } snprintf(bp, buflen - (bp - buf), "%s ", - cb_opcode_name(one_op.argop)); + cb_opcode_name(one_op.argop)); bp += strlen(bp); operand = sum_cb_operand(&one_op); if (strlen(operand) > 0) { @@ -1058,11 +1055,11 @@ detail_nfs_argop4(void) longjmp(xdr_err, 1); (void) sprintf(get_line(0, 0), "Minor version = %u", - minor_version); + minor_version); numops = getxdr_long(); (void) sprintf(get_line(0, 0), "Number of operations = %d", - numops); + numops); while (numops-- > 0) { bzero(&one_op, sizeof (one_op)); @@ -1074,7 +1071,7 @@ detail_nfs_argop4(void) get_line(0, 0); /* blank line to separate ops */ sprintf(get_line(0, 0), "Op = %d (%s)", - one_op.argop, opcode_name(one_op.argop)); + one_op.argop, opcode_name(one_op.argop)); if (one_op.argop < num_opcodes) { fmtproc = opcode_info[one_op.argop].dtlarg; if (fmtproc != NULL) @@ -1104,16 +1101,16 @@ detail_cb_argop4(void) if (!xdr_uint32_t(&xdrm, &minor_version)) longjmp(xdr_err, 1); (void) sprintf(get_line(0, 0), "Minor version = %u", - minor_version); + minor_version); if (!xdr_uint32_t(&xdrm, &callback_ident)) longjmp(xdr_err, 1); (void) sprintf(get_line(0, 0), "Callback Ident = %u", - callback_ident); + callback_ident); numops = getxdr_long(); (void) sprintf(get_line(0, 0), "Number of operations = %d", - numops); + numops); while (numops-- > 0) { bzero(&one_op, sizeof (one_op)); @@ -1124,7 +1121,7 @@ detail_cb_argop4(void) get_line(0, 0); /* blank line to separate ops */ sprintf(get_line(0, 0), "Op = %d (%s)", - one_op.argop, cb_opcode_name(one_op.argop)); + one_op.argop, cb_opcode_name(one_op.argop)); if (one_op.argop < cb_num_opcodes) { fmtproc = cb_opcode_info[one_op.argop].dtlarg; if (fmtproc != NULL) @@ -1268,10 +1265,10 @@ _sum_stateid(stateid4 *stateid, char *prefix) if ((spec = special_stateid(stateid)) < 0) snprintf(buf, sizeof (buf), "%s%04X:%u", prefix, - stateid_hash(stateid), stateid->seqid); + stateid_hash(stateid), stateid->seqid); else snprintf(buf, sizeof (buf), "%s%s", prefix, - spec == 0 ? "SPC0" : (spec == 1 ? "SPC1" : "SPC?")); + spec == 0 ? "SPC0" : (spec == 1 ? "SPC1" : "SPC?")); return (buf); } @@ -1285,15 +1282,15 @@ _detail_stateid(stateid4 *stateid, char *prefix) if (spec < 0) sprintf(get_line(0, 0), "%sState ID hash = %04X", - prefix, stateid_hash(stateid)); + prefix, stateid_hash(stateid)); else sprintf(get_line(0, 0), "%sState ID hash = %s", prefix, - spec == 0 ? "SPECIAL_0" : - (spec == 1 ? "SPECIAL_1" : "SPECIAL_?")); + spec == 0 ? "SPECIAL_0" : + (spec == 1 ? "SPECIAL_1" : "SPECIAL_?")); sprintf(get_line(0, 0), " len = %u val = %s", - sizeof (stateid->other), - tohex(stateid->other, sizeof (stateid->other))); + sizeof (stateid->other), + tohex(stateid->other, sizeof (stateid->other))); /* * If spec 0/1 stateid, print seqid in hex; otherwise, @@ -1307,7 +1304,7 @@ _detail_stateid(stateid4 *stateid, char *prefix) sprintf(seqstr, "%08X", stateid->seqid); sprintf(get_line(0, 0), " %sState ID Sequence ID = %s", - prefix, seqstr); + prefix, seqstr); } @@ -1317,9 +1314,9 @@ sum_lock_denied(LOCK4denied *denied) static char buf[64]; sprintf(buf, "%s %llu %llu LO=%04X", - sum_lock_type_name(denied->locktype), - denied->offset, denied->length, - owner_hash(&denied->owner.owner)); + sum_lock_type_name(denied->locktype), + denied->offset, denied->length, + owner_hash(&denied->owner.owner)); return (buf); } @@ -1368,7 +1365,7 @@ static void detail_createhow4(createhow4 *crtp) { sprintf(get_line(0, 0), "Method = %s", - createhow4_name(crtp)); + createhow4_name(crtp)); switch (crtp->mode) { case UNCHECKED4: @@ -1377,8 +1374,8 @@ detail_createhow4(createhow4 *crtp) break; case EXCLUSIVE4: sprintf(get_line(0, 0), " Verifier = %s", - tohex(crtp->createhow4_u.createverf, - NFS4_VERIFIER_SIZE)); + tohex(crtp->createhow4_u.createverf, + NFS4_VERIFIER_SIZE)); break; } } @@ -1387,17 +1384,17 @@ static void detail_createtype4(createtype4 *crtp) { sprintf(get_line(0, 0), "Type = %s", - detail_type_name(crtp->type)); + detail_type_name(crtp->type)); switch (crtp->type) { case NF4LNK: sprintf(get_line(0, 0), "Linkdata = %s", - utf8localize(&crtp->createtype4_u.linkdata)); + utf8localize(&crtp->createtype4_u.linkdata)); break; case NF4BLK: case NF4CHR: sprintf(get_line(0, 0), "Specdata1 = %04x Specdata2 = %04x", - crtp->createtype4_u.devdata.specdata1, - crtp->createtype4_u.devdata.specdata2); + crtp->createtype4_u.devdata.specdata1, + crtp->createtype4_u.devdata.specdata2); break; default: break; @@ -1426,7 +1423,7 @@ sumarg_close(char *buf, size_t buflen, void *obj) CLOSE4args *args = (CLOSE4args *)obj; snprintf(buf, buflen, "SQ=%u %s", - args->seqid, sum_open_stateid(&args->open_stateid)); + args->seqid, sum_open_stateid(&args->open_stateid)); } static void @@ -1444,7 +1441,7 @@ sumarg_commit(char *buf, size_t buflen, void *obj) COMMIT4args *args = (COMMIT4args *)obj; snprintf(buf, buflen, "at %llu for %u ", args->offset, - args->count); + args->count); } static void @@ -1478,7 +1475,7 @@ sumarg_create(char *buf, size_t buflen, void *obj) CREATE4args *args = (CREATE4args *)obj; snprintf(buf, buflen, "%s %s ", component_name(&args->objname), - sum_type_name(args->objtype.type)); + sum_type_name(args->objtype.type)); } static void @@ -1566,7 +1563,7 @@ sumarg_cb_recall(char *buf, size_t buflen, void *obj) char *bp = buf; snprintf(bp, buflen, "%s %s TR=%s", sum_fh4(&args->fh), - sum_stateid(&args->stateid), args->truncate ? "T" : "F"); + sum_stateid(&args->stateid), args->truncate ? "T" : "F"); } static void @@ -1577,7 +1574,7 @@ dtlarg_cb_recall(void *obj) detail_fh4(&args->fh); detail_stateid(&args->stateid); sprintf(get_line(0, 0), "Truncate = %s", - args->truncate ? "True" : "False"); + args->truncate ? "True" : "False"); } @@ -1608,9 +1605,9 @@ sumarg_open(char *buf, size_t buflen, void *obj) blen -= len; snprintf(bp, blen, " AC=%s DN=%s OO=%04X", - sum_open_share_access(args->share_access), - sum_open_share_deny(args->share_deny), - owner_hash(&args->owner.owner)); + sum_open_share_access(args->share_access), + sum_open_share_deny(args->share_deny), + owner_hash(&args->owner.owner)); } static void @@ -1623,9 +1620,9 @@ dtlarg_open(void *obj) detail_open_owner(&args->owner); sprintf(get_line(0, 0), "Sequence ID = %u", args->seqid); sprintf(get_line(0, 0), "Access = 0x%x (%s)", - args->share_access, sum_open_share_access(args->share_access)); + args->share_access, sum_open_share_access(args->share_access)); sprintf(get_line(0, 0), "Deny = 0x%x (%s)", - args->share_deny, sum_open_share_access(args->share_deny)); + args->share_deny, sum_open_share_access(args->share_deny)); } static void @@ -1634,7 +1631,7 @@ sumarg_openattr(char *buf, size_t buflen, void *obj) OPENATTR4args *args = (OPENATTR4args *)obj; snprintf(buf, buflen, "CD=%s", - args->createdir ? "T" : "F"); + args->createdir ? "T" : "F"); } static void @@ -1643,7 +1640,7 @@ dtlarg_openattr(void *obj) OPENATTR4args *args = (OPENATTR4args *)obj; sprintf(get_line(0, 0), "CreateDir = %s", - args->createdir ? "True" : "False"); + args->createdir ? "True" : "False"); } static void @@ -1653,7 +1650,7 @@ sumarg_open_confirm(char *buf, size_t buflen, void *obj) OPEN_CONFIRM4args *args = (OPEN_CONFIRM4args *)obj; snprintf(bp, buflen, "SQ=%u %s", args->seqid, - sum_open_stateid(&args->open_stateid)); + sum_open_stateid(&args->open_stateid)); } static void @@ -1671,9 +1668,9 @@ sumarg_open_downgrd(char *buf, size_t buflen, void *obj) OPEN_DOWNGRADE4args *args = (OPEN_DOWNGRADE4args *)obj; snprintf(buf, buflen, "SQ=%u %s AC=%s DN=%s", - args->seqid, sum_open_stateid(&args->open_stateid), - sum_open_share_access(args->share_access), - sum_open_share_deny(args->share_deny)); + args->seqid, sum_open_stateid(&args->open_stateid), + sum_open_share_access(args->share_access), + sum_open_share_deny(args->share_deny)); } static void @@ -1684,9 +1681,9 @@ dtlarg_open_downgrd(void *obj) sprintf(get_line(0, 0), "Open Sequence ID = %u", args->seqid); detail_open_stateid(&args->open_stateid); sprintf(get_line(0, 0), "Access = 0x%x (%s)", - args->share_access, sum_open_share_access(args->share_access)); + args->share_access, sum_open_share_access(args->share_access)); sprintf(get_line(0, 0), "Deny = 0x%x (%s)", - args->share_deny, sum_open_share_access(args->share_deny)); + args->share_deny, sum_open_share_access(args->share_deny)); } static void @@ -1719,22 +1716,22 @@ dtlarg_link(void *obj) LINK4args *args = (LINK4args *)obj; sprintf(get_line(0, 0), "New name = %s", - component_name(&args->newname)); + component_name(&args->newname)); } static void sum_open_to_lock_owner(char *buf, int buflen, open_to_lock_owner4 *own) { snprintf(buf, buflen, " OSQ=%u %s LSQ=%u LO=%04X", own->open_seqid, - sum_open_stateid(&own->open_stateid), own->lock_seqid, - owner_hash(&own->lock_owner.owner)); + sum_open_stateid(&own->open_stateid), own->lock_seqid, + owner_hash(&own->lock_owner.owner)); } static void sum_exist_lock_owner(char *buf, int buflen, exist_lock_owner4 *own) { snprintf(buf, buflen, " LSQ=%u %s", own->lock_seqid, - sum_lock_stateid(&own->lock_stateid)); + sum_lock_stateid(&own->lock_stateid)); } static void @@ -1779,9 +1776,9 @@ sumarg_lock(char *buf, size_t buflen, void *obj) char *bp = buf; snprintf(buf, buflen, "%s%s%llu:%llu", - sum_lock_type_name(args->locktype), - args->reclaim ? " reclaim " : " ", - args->offset, args->length); + sum_lock_type_name(args->locktype), + args->reclaim ? " reclaim " : " ", + args->offset, args->length); bp += strlen(buf); sum_locker(bp, buflen - (bp - buf), &args->locker); @@ -1819,7 +1816,7 @@ dtlarg_lock(void *obj) sprintf(get_line(0, 0), "Type = %s", lock_type_name(args->locktype)); sprintf(get_line(0, 0), "Reclaim = %s", - args->reclaim ? "TRUE" : "FALSE"); + args->reclaim ? "TRUE" : "FALSE"); sprintf(get_line(0, 0), "Offset = %llu", args->offset); sprintf(get_line(0, 0), "Length = %llu", args->length); detail_locker(&args->locker); @@ -1831,7 +1828,7 @@ sumarg_lockt(char *buf, size_t buflen, void *obj) LOCKT4args *args = (LOCKT4args *)obj; snprintf(buf, buflen, "R=%llu:%llu", - args->offset, args->length); + args->offset, args->length); } static void @@ -1851,8 +1848,8 @@ sumarg_locku(char *buf, size_t buflen, void *obj) LOCKU4args *args = (LOCKU4args *)obj; snprintf(buf, buflen, "R=%llu:%llu LSQ=%u %s", - args->offset, args->length, args->seqid, - sum_lock_stateid(&args->lock_stateid)); + args->offset, args->length, args->seqid, + sum_lock_stateid(&args->lock_stateid)); } @@ -1890,7 +1887,7 @@ sumarg_read(char *buf, size_t buflen, void *obj) READ4args *args = (READ4args *)obj; snprintf(buf, buflen, "%s at %llu for %u", - sum_stateid(&args->stateid), args->offset, args->count); + sum_stateid(&args->stateid), args->offset, args->count); } static void @@ -1909,8 +1906,8 @@ sumarg_readdir(char *buf, size_t buflen, void *obj) READDIR4args *args = (READDIR4args *)obj; snprintf(buf, buflen, "Cookie=%llu (%s) for %u/%u", - args->cookie, tohex(args->cookieverf, NFS4_VERIFIER_SIZE), - args->dircount, args->maxcount); + args->cookie, tohex(args->cookieverf, NFS4_VERIFIER_SIZE), + args->dircount, args->maxcount); } static void @@ -1920,7 +1917,7 @@ dtlarg_readdir(void *obj) sprintf(get_line(0, 0), "Cookie = %llu", args->cookie); sprintf(get_line(0, 0), "Verifier = %s", - tohex(args->cookieverf, NFS4_VERIFIER_SIZE)); + tohex(args->cookieverf, NFS4_VERIFIER_SIZE)); sprintf(get_line(0, 0), "Dircount = %u", args->dircount); sprintf(get_line(0, 0), "Maxcount = %u", args->maxcount); detail_attr_bitmap("", &args->attr_request, NULL); @@ -1949,8 +1946,8 @@ sumarg_rename(char *buf, size_t buflen, void *obj) RENAME4args *args = (RENAME4args *)obj; snprintf(buf, buflen, "%s to %s", - component_name(&args->oldname), - component_name(&args->newname)); + component_name(&args->oldname), + component_name(&args->newname)); } static void @@ -1959,9 +1956,9 @@ dtlarg_rename(void *obj) RENAME4args *args = (RENAME4args *)obj; sprintf(get_line(0, 0), "Old name = %s", - component_name(&args->oldname)); + component_name(&args->oldname)); sprintf(get_line(0, 0), "New name = %s", - component_name(&args->newname)); + component_name(&args->newname)); } static void @@ -1985,7 +1982,7 @@ sumarg_secinfo(char *buf, size_t buflen, void *obj) SECINFO4args *args = (SECINFO4args *)obj; snprintf(buf, buflen, "%s", - component_name(&args->name)); + component_name(&args->name)); } static void @@ -1994,7 +1991,7 @@ dtlarg_secinfo(void *obj) SECINFO4args *args = (SECINFO4args *)obj; sprintf(get_line(0, 0), "Name = %s", - component_name(&args->name)); + component_name(&args->name)); } static void @@ -2020,9 +2017,9 @@ sumarg_setclid(char *buf, size_t buflen, void *obj) SETCLIENTID4args *args = (SETCLIENTID4args *)obj; snprintf(buf, buflen, "Prog=%u ID=%s Addr=%s CBID=%u", - args->callback.cb_program, - args->callback.cb_location.r_netid, - args->callback.cb_location.r_addr, args->callback_ident); + args->callback.cb_program, + args->callback.cb_location.r_netid, + args->callback.cb_location.r_addr, args->callback_ident); } static void @@ -2031,17 +2028,17 @@ dtlarg_setclid(void *obj) SETCLIENTID4args *args = (SETCLIENTID4args *)obj; sprintf(get_line(0, 0), "Verifier=%s", - tohex(args->client.verifier, NFS4_VERIFIER_SIZE)); + tohex(args->client.verifier, NFS4_VERIFIER_SIZE)); sprintf(get_line(0, 0), "ID = (%d) %s", - args->client.id.id_len, - tohex(args->client.id.id_val, args->client.id.id_len)); + args->client.id.id_len, + tohex(args->client.id.id_val, args->client.id.id_len)); sprintf(get_line(0, 0), "Callback Program = %u", - args->callback.cb_program); + args->callback.cb_program); sprintf(get_line(0, 0), "Callback Net ID = %s", - args->callback.cb_location.r_netid); + args->callback.cb_location.r_netid); sprintf(get_line(0, 0), "Callback Addr = %s", - args->callback.cb_location.r_addr); + args->callback.cb_location.r_addr); sprintf(get_line(0, 0), "Callback Ident = %u", args->callback_ident); } @@ -2051,7 +2048,7 @@ sumarg_setclid_cfm(char *buf, size_t buflen, void *obj) SETCLIENTID_CONFIRM4args *args = (SETCLIENTID_CONFIRM4args *)obj; snprintf(buf, buflen, "%s CFV=%s", sum_clientid(args->clientid), - tohex(args->setclientid_confirm, NFS4_VERIFIER_SIZE)); + tohex(args->setclientid_confirm, NFS4_VERIFIER_SIZE)); } static void @@ -2061,7 +2058,7 @@ dtlarg_setclid_cfm(void *obj) detail_clientid(args->clientid); sprintf(get_line(0, 0), "Set Client ID Confirm Verifier = %s", - tohex(args->setclientid_confirm, NFS4_VERIFIER_SIZE)); + tohex(args->setclientid_confirm, NFS4_VERIFIER_SIZE)); } @@ -2079,7 +2076,7 @@ sumarg_write(char *buf, size_t buflen, void *obj) WRITE4args *args = (WRITE4args *)obj; snprintf(buf, buflen, "%s at %llu for %u", - sum_stateid(&args->stateid), args->offset, args->data.data_len); + sum_stateid(&args->stateid), args->offset, args->data.data_len); } static void @@ -2134,7 +2131,7 @@ detail_fattr4(fattr4 *attrp) jmp_buf old_errbuf; xdrmem_create(&attrxdr, attrp->attr_vals.attrlist4_val, - attrp->attr_vals.attrlist4_len, XDR_DECODE); + attrp->attr_vals.attrlist4_len, XDR_DECODE); bcopy(xdr_err, old_errbuf, sizeof (old_errbuf)); if (setjmp(xdr_err)) { @@ -2171,8 +2168,8 @@ sum_attr_bitmap(char *buf, size_t buflen, bitmap4 *mapp) bp = buf + curlen; remaining = buflen - curlen; snprintf(bp, remaining, - num_words == 0 ? "%x" : " %x", - mapp->bitmap4_val[num_words]); + num_words == 0 ? "%x" : " %x", + mapp->bitmap4_val[num_words]); } } @@ -2216,7 +2213,7 @@ detail_attr_bitmap(char *prefix, bitmap4 *bitp, unpkd_attrmap_t *unpacked) attr_name(attrnum + bit)); if (unpacked != NULL) unpacked->map[attrnum + bit] = - 1; + 1; } } } @@ -2238,7 +2235,7 @@ sum_comp4res(char *line, char *(*sumres_fn)(void)) longjmp(xdr_err, 1); sprintf(line, "(%.20s) %s %s", utf8localize(&tag), - status_name(status), sumres_fn()); + status_name(status), sumres_fn()); xdr_free(xdr_utf8string, (char *)&tag); } @@ -2263,7 +2260,7 @@ sum_compound4res(void) if (setjmp(xdr_err)) { bp = buf + strlen(buf); snprintf(bp, buflen - (bp - buf), - nfs4_fragged_rpc ? nfs4err_fragrpc : nfs4err_xdrfrag); + nfs4_fragged_rpc ? nfs4err_fragrpc : nfs4err_xdrfrag); return (buf); } @@ -2280,7 +2277,7 @@ sum_compound4res(void) } snprintf(bp, buflen - (bp - buf), "%s ", - opcode_name(one_res.resop)); + opcode_name(one_res.resop)); bp += strlen(bp); result = sum_result(&one_res); @@ -2325,7 +2322,7 @@ sum_cb_compound4res(void) if (setjmp(xdr_err)) { bp = buf + strlen(buf); snprintf(bp, buflen - (bp - buf), "<XDR Error or Fragmented" - " RPC>"); + " RPC>"); return (buf); } @@ -2338,8 +2335,8 @@ sum_cb_compound4res(void) longjmp(xdr_err, 1); } snprintf(bp, buflen - (bp - buf), "%s %s ", - cb_opcode_name(one_res.resop), - sum_cb_result(&one_res)); + cb_opcode_name(one_res.resop), + sum_cb_result(&one_res)); bp += strlen(bp); xdr_free(xdr_nfs_cb_resop4, (char *)&one_res); @@ -2410,7 +2407,7 @@ dtl_change_info(char *msg, change_info4 *infop) { sprintf(get_line(0, 0), "%s:", msg); sprintf(get_line(0, 0), " Atomic = %s", - infop->atomic ? "TRUE" : "FALSE"); + infop->atomic ? "TRUE" : "FALSE"); detail_fattr4_change(" Before", infop->before); detail_fattr4_change(" After", infop->after); } @@ -2436,7 +2433,7 @@ dtl_nfsstat4(void *obj) nfsstat4 status = *(nfsstat4 *)obj; sprintf(get_line(0, 0), "Status = %d (%s)", status, - status_name(status)); + status_name(status)); } static void @@ -2475,9 +2472,9 @@ dtlres_access(void *obj) dtl_nfsstat4(obj); if (res->status == NFS4_OK) { detail_access4("Supported Attributes", - res->ACCESS4res_u.resok4.supported); + res->ACCESS4res_u.resok4.supported); detail_access4("Allowed Attributes", - res->ACCESS4res_u.resok4.access); + res->ACCESS4res_u.resok4.access); } } @@ -2488,7 +2485,7 @@ sumres_close(char *buf, size_t buflen, void *obj) if (res->status == NFS4_OK) snprintf(buf, buflen, "%s", - sum_open_stateid(&res->CLOSE4res_u.open_stateid)); + sum_open_stateid(&res->CLOSE4res_u.open_stateid)); } static void @@ -2509,8 +2506,8 @@ sumres_commit(char *buf, size_t buflen, void *obj) if (res->status == NFS4_OK) snprintf(buf, buflen, "Verf=%s", - tohex(res->COMMIT4res_u.resok4.writeverf, - NFS4_VERIFIER_SIZE)); + tohex(res->COMMIT4res_u.resok4.writeverf, + NFS4_VERIFIER_SIZE)); } static void @@ -2521,8 +2518,8 @@ dtlres_commit(void *obj) dtl_nfsstat4(obj); if (res->status == NFS4_OK) { sprintf(get_line(0, 0), "Verifier = %s", - tohex(res->COMMIT4res_u.resok4.writeverf, - NFS4_VERIFIER_SIZE)); + tohex(res->COMMIT4res_u.resok4.writeverf, + NFS4_VERIFIER_SIZE)); } } @@ -2534,9 +2531,9 @@ dtlres_create(void *obj) dtl_nfsstat4(obj); if (res->status == NFS4_OK) { dtl_change_info("Change Information", - &res->CREATE4res_u.resok4.cinfo); + &res->CREATE4res_u.resok4.cinfo); detail_attr_bitmap("", &res->CREATE4res_u.resok4.attrset, - NULL); + NULL); } } @@ -2589,7 +2586,7 @@ sumres_getfh(char *buf, size_t buflen, void *obj) if (res->status == NFS4_OK) { bp = buf + strlen(buf); snprintf(bp, buflen - (bp - buf), " %s", - sum_fh4(&res->GETFH4res_u.resok4.object)); + sum_fh4(&res->GETFH4res_u.resok4.object)); } } @@ -2612,7 +2609,7 @@ dtlres_link(void *obj) dtl_nfsstat4(obj); if (res->status == NFS4_OK) { dtl_change_info("Change Information", - &res->LINK4res_u.resok4.cinfo); + &res->LINK4res_u.resok4.cinfo); } } @@ -2626,12 +2623,12 @@ sumres_lock(char *buf, size_t buflen, void *obj) if (res->status == NFS4_OK) { bp = buf + strlen(buf); snprintf(bp, buflen - (bp - buf), " %s", - sum_lock_stateid(&res->LOCK4res_u.resok4.lock_stateid)); + sum_lock_stateid(&res->LOCK4res_u.resok4.lock_stateid)); } if (res->status == NFS4ERR_DENIED) { bp = buf + strlen(buf); snprintf(bp, buflen - (bp - buf), " %s", - sum_lock_denied(&res->LOCK4res_u.denied)); + sum_lock_denied(&res->LOCK4res_u.denied)); } } @@ -2659,7 +2656,7 @@ sumres_lockt(char *buf, size_t buflen, void *obj) if (res->status == NFS4ERR_DENIED) { bp = buf + strlen(buf); snprintf(bp, buflen - (bp - buf), " %s", - sum_lock_denied(&res->LOCKT4res_u.denied)); + sum_lock_denied(&res->LOCKT4res_u.denied)); } } @@ -2684,7 +2681,7 @@ sumres_locku(char *buf, size_t buflen, void *obj) bp = buf + strlen(buf); if (res->status == NFS4_OK) snprintf(bp, buflen - (bp - buf), " %s", - sum_lock_stateid(&res->LOCKU4res_u.lock_stateid)); + sum_lock_stateid(&res->LOCKU4res_u.lock_stateid)); } static void @@ -2712,7 +2709,7 @@ sumres_open(char *buf, size_t buflen, void *obj) blen -= len; snprintf(bp, blen, " %s", - sum_stateid(&res->OPEN4res_u.resok4.stateid)); + sum_stateid(&res->OPEN4res_u.resok4.stateid)); bp += (len = strlen(bp)); blen -= len; @@ -2735,12 +2732,12 @@ dtlres_open(void *obj) if (res->status == NFS4_OK) { detail_stateid(&res->OPEN4res_u.resok4.stateid); dtl_change_info("Change Information", - &res->OPEN4res_u.resok4.cinfo); + &res->OPEN4res_u.resok4.cinfo); sprintf(get_line(0, 0), "Flags = 0x%x (%s)", - res->OPEN4res_u.resok4.rflags, - detail_open_rflags(res->OPEN4res_u.resok4.rflags)); + res->OPEN4res_u.resok4.rflags, + detail_open_rflags(res->OPEN4res_u.resok4.rflags)); detail_attr_bitmap("", &res->OPEN4res_u.resok4.attrset, - NULL); + NULL); detail_delegation(&res->OPEN4res_u.resok4.delegation); } } @@ -2755,8 +2752,8 @@ sumres_open_confirm(char *buf, size_t buflen, void *obj) if (res->status == NFS4_OK) { bp = buf + strlen(buf); snprintf(bp, buflen - (bp - buf), " %s", - sum_open_stateid(&res->OPEN_CONFIRM4res_u.resok4. - open_stateid)); + sum_open_stateid(&res->OPEN_CONFIRM4res_u.resok4. + open_stateid)); } } @@ -2768,7 +2765,7 @@ dtlres_open_confirm(void *obj) dtl_nfsstat4(obj); if (res->status == NFS4_OK) { detail_open_stateid(&res->OPEN_CONFIRM4res_u.resok4. - open_stateid); + open_stateid); } } @@ -2782,8 +2779,8 @@ sumres_open_downgrd(char *buf, size_t buflen, void *obj) if (res->status == NFS4_OK) { bp = buf + strlen(buf); snprintf(bp, buflen - (bp - buf), " %s", - sum_open_stateid(&res->OPEN_DOWNGRADE4res_u.resok4. - open_stateid)); + sum_open_stateid(&res->OPEN_DOWNGRADE4res_u.resok4. + open_stateid)); } } @@ -2795,7 +2792,7 @@ dtlres_open_downgrd(void *obj) dtl_nfsstat4(obj); if (res->status == NFS4_OK) { detail_open_stateid(&res->OPEN_DOWNGRADE4res_u.resok4. - open_stateid); + open_stateid); } } @@ -2809,8 +2806,8 @@ sumres_read(char *buf, size_t buflen, void *obj) if (res->status == NFS4_OK) { bp = buf + strlen(buf); snprintf(bp, buflen - (bp - buf), " (%u bytes) %s", - res->READ4res_u.resok4.data.data_len, - res->READ4res_u.resok4.eof ? "EOF" : ""); + res->READ4res_u.resok4.data.data_len, + res->READ4res_u.resok4.eof ? "EOF" : ""); } } @@ -2822,9 +2819,9 @@ dtlres_read(void *obj) dtl_nfsstat4(obj); if (res->status == NFS4_OK) { sprintf(get_line(0, 0), "Count = %u bytes read", - res->READ4res_u.resok4.data.data_len); + res->READ4res_u.resok4.data.data_len); sprintf(get_line(0, 0), "End of file = %s", - res->READ4res_u.resok4.eof ? "TRUE" : "FALSE"); + res->READ4res_u.resok4.eof ? "TRUE" : "FALSE"); } } @@ -2844,9 +2841,9 @@ sumres_readdir(char *buf, size_t buflen, void *obj) num_entries++; bp = buf + strlen(buf); snprintf(bp, buflen - (bp - buf), " %d entries (%s)", - num_entries, - res->READDIR4res_u.resok4.reply.eof - ? "No more" : "More"); + num_entries, + res->READDIR4res_u.resok4.reply.eof + ? "No more" : "More"); } } @@ -2864,12 +2861,12 @@ dtlres_readdir(void *obj) ep = ep->nextentry) { num_entries++; sprintf(get_line(0, 0), - "------------------ entry #%d", - num_entries); + "------------------ entry #%d", + num_entries); sprintf(get_line(0, 0), "Cookie = %llu", - ep->cookie); + ep->cookie); sprintf(get_line(0, 0), "Name = %s", - component_name(&ep->name)); + component_name(&ep->name)); detail_fattr4(&ep->attrs); } if (num_entries == 0) @@ -2877,8 +2874,8 @@ dtlres_readdir(void *obj) sprintf(get_line(0, 0), "EOF = %s", res->READDIR4res_u.resok4.reply.eof ? "TRUE" : "FALSE"); sprintf(get_line(0, 0), "Verifer = %s", - tohex(res->READDIR4res_u.resok4.cookieverf, - NFS4_VERIFIER_SIZE)); + tohex(res->READDIR4res_u.resok4.cookieverf, + NFS4_VERIFIER_SIZE)); } } @@ -2892,7 +2889,7 @@ sumres_readlnk(char *buf, size_t buflen, void *obj) if (res->status == NFS4_OK) { bp = buf + strlen(buf); snprintf(bp, buflen - (bp - buf), " %s", - linktext_name(&res->READLINK4res_u.resok4.link)); + linktext_name(&res->READLINK4res_u.resok4.link)); } } @@ -2904,7 +2901,7 @@ dtlres_readlnk(void *obj) dtl_nfsstat4(obj); if (res->status == NFS4_OK) { sprintf(get_line(0, 0), "Link = %s", - linktext_name(&res->READLINK4res_u.resok4.link)); + linktext_name(&res->READLINK4res_u.resok4.link)); } } @@ -2916,7 +2913,7 @@ dtlres_remove(void *obj) dtl_nfsstat4(obj); if (res->status == NFS4_OK) { dtl_change_info("Change Information", - &res->REMOVE4res_u.resok4.cinfo); + &res->REMOVE4res_u.resok4.cinfo); } } @@ -2928,9 +2925,9 @@ dtlres_rename(void *obj) dtl_nfsstat4(obj); if (res->status == NFS4_OK) { dtl_change_info("Source Change Information", - &res->RENAME4res_u.resok4.source_cinfo); + &res->RENAME4res_u.resok4.source_cinfo); dtl_change_info("Target Change Information", - &res->RENAME4res_u.resok4.target_cinfo); + &res->RENAME4res_u.resok4.target_cinfo); } } @@ -2950,7 +2947,7 @@ sumres_secinfo(char *buf, size_t buflen, void *obj) numinfo != 0; infop++, numinfo--) { snprintf(bp, buflen - (bp - buf), " %s", - flavor_name(infop->flavor)); + flavor_name(infop->flavor)); bp += strlen(bp); } } @@ -2964,7 +2961,7 @@ dtlres_secinfo(void *obj) dtl_nfsstat4(obj); if (res->status == NFS4_OK) { uint_t numinfo = - res->SECINFO4res_u.resok4.SECINFO4resok_len; + res->SECINFO4res_u.resok4.SECINFO4resok_len; secinfo4 *infop; for (infop = res->SECINFO4res_u.resok4.SECINFO4resok_val; @@ -3004,15 +3001,15 @@ sumres_setclid(char *buf, size_t buflen, void *obj) case NFS_OK: bp = buf + strlen(buf); snprintf(bp, buflen - (bp - buf), " %s CFV=%s", - sum_clientid(res->SETCLIENTID4res_u.resok4.clientid), - tohex(res->SETCLIENTID4res_u.resok4.setclientid_confirm, - NFS4_VERIFIER_SIZE)); + sum_clientid(res->SETCLIENTID4res_u.resok4.clientid), + tohex(res->SETCLIENTID4res_u.resok4.setclientid_confirm, + NFS4_VERIFIER_SIZE)); break; case NFS4ERR_CLID_INUSE: bp = buf + strlen(buf); snprintf(bp, buflen - (bp - buf), " ID=%s Addr=%s", - res->SETCLIENTID4res_u.client_using.r_netid, - res->SETCLIENTID4res_u.client_using.r_addr); + res->SETCLIENTID4res_u.client_using.r_netid, + res->SETCLIENTID4res_u.client_using.r_addr); break; } } @@ -3027,14 +3024,14 @@ dtlres_setclid(void *obj) case NFS_OK: detail_clientid(res->SETCLIENTID4res_u.resok4.clientid); sprintf(get_line(0, 0), "Set Client ID Confirm Verifier = %s", - tohex(res->SETCLIENTID4res_u.resok4.setclientid_confirm, - NFS4_VERIFIER_SIZE)); + tohex(res->SETCLIENTID4res_u.resok4.setclientid_confirm, + NFS4_VERIFIER_SIZE)); break; case NFS4ERR_CLID_INUSE: sprintf(get_line(0, 0), "Used by Net ID = %s", - res->SETCLIENTID4res_u.client_using.r_netid); + res->SETCLIENTID4res_u.client_using.r_netid); sprintf(get_line(0, 0), "Used by Addr = %s", - res->SETCLIENTID4res_u.client_using.r_addr); + res->SETCLIENTID4res_u.client_using.r_addr); break; } } @@ -3049,8 +3046,8 @@ sumres_write(char *buf, size_t buflen, void *obj) if (res->status == NFS4_OK) { bp = buf + strlen(buf); snprintf(bp, buflen - (bp - buf), " %u (%s)", - res->WRITE4res_u.resok4.count, - stable_how4_name(res->WRITE4res_u.resok4.committed)); + res->WRITE4res_u.resok4.count, + stable_how4_name(res->WRITE4res_u.resok4.committed)); } } @@ -3062,12 +3059,12 @@ dtlres_write(void *obj) dtl_nfsstat4(obj); if (res->status == NFS4_OK) { sprintf(get_line(0, 0), "Count = %u bytes written", - res->WRITE4res_u.resok4.count); + res->WRITE4res_u.resok4.count); sprintf(get_line(0, 0), "Stable = %s", - stable_how4_name(res->WRITE4res_u.resok4.committed)); + stable_how4_name(res->WRITE4res_u.resok4.committed)); sprintf(get_line(0, 0), "Verifier = %s", - tohex(res->WRITE4res_u.resok4.writeverf, - NFS4_VERIFIER_SIZE)); + tohex(res->WRITE4res_u.resok4.writeverf, + NFS4_VERIFIER_SIZE)); } } @@ -3084,7 +3081,7 @@ detail_nfs_resop4(void) numres = getxdr_long(); (void) sprintf(get_line(0, 0), "Number of results = %d", - numres); + numres); while (numres-- > 0) { bzero(&one_res, sizeof (one_res)); @@ -3096,7 +3093,7 @@ detail_nfs_resop4(void) get_line(0, 0); /* blank line to separate ops */ sprintf(get_line(0, 0), "Op = %d (%s)", - one_res.resop, opcode_name(one_res.resop)); + one_res.resop, opcode_name(one_res.resop)); if (one_res.resop < num_opcodes) fmtproc = opcode_info[one_res.resop].dtlres; else if (one_res.resop == OP_ILLEGAL) @@ -3129,7 +3126,7 @@ detail_cb_resop4(void) numres = getxdr_long(); (void) sprintf(get_line(0, 0), "Number of results = %d", - numres); + numres); while (numres-- > 0) { bzero(&one_res, sizeof (one_res)); @@ -3138,7 +3135,7 @@ detail_cb_resop4(void) get_line(0, 0); /* blank line to separate ops */ sprintf(get_line(0, 0), "Op = %d (%s)", - one_res.resop, cb_opcode_name(one_res.resop)); + one_res.resop, cb_opcode_name(one_res.resop)); if (one_res.resop < cb_num_opcodes) fmtproc = cb_opcode_info[one_res.resop].dtlres; else if (one_res.resop == OP_CB_ILLEGAL) @@ -3256,17 +3253,17 @@ detail_access4(char *descrip, uint32_t bits) sprintf(get_line(0, 0), "%s = 0x%08x", descrip, bits); (void) sprintf(get_line(0, 0), " %s", - getflag(bits, ACCESS4_READ, "Read", "(no read)")); + getflag(bits, ACCESS4_READ, "Read", "(no read)")); (void) sprintf(get_line(0, 0), " %s", - getflag(bits, ACCESS4_LOOKUP, "Lookup", "(no lookup)")); + getflag(bits, ACCESS4_LOOKUP, "Lookup", "(no lookup)")); (void) sprintf(get_line(0, 0), " %s", - getflag(bits, ACCESS4_MODIFY, "Modify", "(no modify)")); + getflag(bits, ACCESS4_MODIFY, "Modify", "(no modify)")); (void) sprintf(get_line(0, 0), " %s", - getflag(bits, ACCESS4_EXTEND, "Extend", "(no extend)")); + getflag(bits, ACCESS4_EXTEND, "Extend", "(no extend)")); (void) sprintf(get_line(0, 0), " %s", - getflag(bits, ACCESS4_DELETE, "Delete", "(no delete)")); + getflag(bits, ACCESS4_DELETE, "Delete", "(no delete)")); (void) sprintf(get_line(0, 0), " %s", - getflag(bits, ACCESS4_EXECUTE, "Execute", "(no execute)")); + getflag(bits, ACCESS4_EXECUTE, "Execute", "(no execute)")); } @@ -3281,19 +3278,19 @@ sum_name(char *buf, size_t buflen, open_claim4 *claim) switch (claim->claim) { case CLAIM_NULL: snprintf(bp, buflen, "%s ", - component_name(&claim->open_claim4_u.file)); + component_name(&claim->open_claim4_u.file)); break; case CLAIM_PREVIOUS: break; case CLAIM_DELEGATE_CUR: snprintf(bp, buflen, "%s ", - component_name(&claim->open_claim4_u. - delegate_cur_info.file)); + component_name(&claim->open_claim4_u. + delegate_cur_info.file)); break; case CLAIM_DELEGATE_PREV: snprintf(bp, buflen, "%s ", - component_name(&claim->open_claim4_u. - file_delegate_prev)); + component_name(&claim->open_claim4_u. + file_delegate_prev)); break; } } @@ -3312,12 +3309,12 @@ sum_claim(char *buf, size_t buflen, open_claim4 *claim) break; case CLAIM_PREVIOUS: snprintf(bp, buflen, " CT=P DT=%s", - get_deleg_typestr(claim->open_claim4_u.delegate_type)); + get_deleg_typestr(claim->open_claim4_u.delegate_type)); break; case CLAIM_DELEGATE_CUR: snprintf(bp, buflen, " CT=DC %s", - sum_deleg_stateid(&claim->open_claim4_u. - delegate_cur_info.delegate_stateid)); + sum_deleg_stateid(&claim->open_claim4_u. + delegate_cur_info.delegate_stateid)); break; case CLAIM_DELEGATE_PREV: snprintf(bp, buflen, " CT=DP"); @@ -3358,7 +3355,7 @@ static void detail_claim(open_claim4 *claim) { sprintf(get_line(0, 0), "Claim Type = %d (%s)", - claim->claim, claim_name(claim->claim)); + claim->claim, claim_name(claim->claim)); switch (claim->claim) { case CLAIM_NULL: @@ -3366,13 +3363,13 @@ detail_claim(open_claim4 *claim) break; case CLAIM_PREVIOUS: sprintf(get_line(0, 0), "Delegate Type = %s (val = %d)", - get_deleg_typestr(claim->open_claim4_u.delegate_type), - claim->open_claim4_u.delegate_type); + get_deleg_typestr(claim->open_claim4_u.delegate_type), + claim->open_claim4_u.delegate_type); break; case CLAIM_DELEGATE_CUR: detail_compname4(&claim->open_claim4_u.delegate_cur_info.file); detail_deleg_stateid(&claim->open_claim4_u.delegate_cur_info. - delegate_stateid); + delegate_stateid); break; case CLAIM_DELEGATE_PREV: detail_compname4(&claim->open_claim4_u.file_delegate_prev); @@ -3415,15 +3412,15 @@ sum_delegation(char *buf, size_t buflen, open_delegation4 *delp) break; case OPEN_DELEGATE_READ: snprintf(buf, buflen, " DT=R %s", - sum_deleg_stateid(&delp->open_delegation4_u.write. - stateid)); + sum_deleg_stateid(&delp->open_delegation4_u.write. + stateid)); break; case OPEN_DELEGATE_WRITE: snprintf(buf, buflen, " DT=W %s %s", - sum_deleg_stateid(&delp->open_delegation4_u.write. - stateid), - sum_space_limit(&delp->open_delegation4_u.write. - space_limit)); + sum_deleg_stateid(&delp->open_delegation4_u.write. + stateid), + sum_space_limit(&delp->open_delegation4_u.write. + space_limit)); break; default: snprintf(buf, buflen, " DT=?"); @@ -3435,8 +3432,8 @@ static void detail_delegation(open_delegation4 *delp) { sprintf(get_line(0, 0), "Delegation Type = %d (%s)", - delp->delegation_type, - delegation_type_name(delp->delegation_type)); + delp->delegation_type, + delegation_type_name(delp->delegation_type)); switch (delp->delegation_type) { case OPEN_DELEGATE_NONE: @@ -3445,17 +3442,17 @@ detail_delegation(open_delegation4 *delp) case OPEN_DELEGATE_READ: detail_deleg_stateid(&delp->open_delegation4_u.read.stateid); sprintf(get_line(0, 0), "Recall = %s", - delp->open_delegation4_u.read.recall ? - "TRUE" : "FALSE"); + delp->open_delegation4_u.read.recall ? + "TRUE" : "FALSE"); sprintf(get_line(0, 0), "[nfsacl4]"); break; case OPEN_DELEGATE_WRITE: detail_deleg_stateid(&delp->open_delegation4_u.write.stateid); sprintf(get_line(0, 0), "Recall = %s", - delp->open_delegation4_u.write.recall ? - "TRUE" : "FALSE"); + delp->open_delegation4_u.write.recall ? + "TRUE" : "FALSE"); detail_space_limit(&delp->open_delegation4_u.write. - space_limit); + space_limit); sprintf(get_line(0, 0), "[nfsacl4]"); break; } @@ -3466,10 +3463,10 @@ static void detail_open_owner(open_owner4 *owner) { sprintf(get_line(0, 0), "Open Owner hash = [%04X] ", - owner_hash(&owner->owner)); + owner_hash(&owner->owner)); sprintf(get_line(0, 0), " len = %u val = %s ", - owner->owner.owner_len, - tohex(owner->owner.owner_val, owner->owner.owner_len)); + owner->owner.owner_len, + tohex(owner->owner.owner_val, owner->owner.owner_len)); detail_clientid(owner->clientid); } @@ -3477,10 +3474,10 @@ static void detail_lock_owner(lock_owner4 *owner) { sprintf(get_line(0, 0), "Lock Owner hash = [%04X] ", - owner_hash(&owner->owner)); + owner_hash(&owner->owner)); sprintf(get_line(0, 0), " len = %u val = %s ", - owner->owner.owner_len, - tohex(owner->owner.owner_val, owner->owner.owner_len)); + owner->owner.owner_len, + tohex(owner->owner.owner_val, owner->owner.owner_len)); detail_clientid(owner->clientid); } @@ -3500,7 +3497,7 @@ sum_openflag(char *bufp, int buflen, openflag4 *flagp) break; default: snprintf(bufp, buflen, "OT=CR(?:%d)", - flagp->openflag4_u.how.mode); + flagp->openflag4_u.how.mode); break; } } else @@ -3511,7 +3508,7 @@ static void detail_openflag(openflag4 *flagp) { sprintf(get_line(0, 0), "Open Type = %s", - flagp->opentype == OPEN4_CREATE ? "CREATE" : "NOCREATE"); + flagp->opentype == OPEN4_CREATE ? "CREATE" : "NOCREATE"); if (flagp->opentype == OPEN4_CREATE) detail_createhow4(&flagp->openflag4_u.how); } @@ -3528,8 +3525,8 @@ sum_pathname4(char *buf, size_t buflen, pathname4 *pathp) for (component = 0; component < pathp->pathname4_len; component++) { snprintf(bp, buflen - (bp - buf), - component == 0 ? "%s" : "/%s", - component_name(&pathp->pathname4_val[component])); + component == 0 ? "%s" : "/%s", + component_name(&pathp->pathname4_val[component])); bp += strlen(bp); } } @@ -3547,17 +3544,17 @@ detail_compname4(component4 *comp) } static void -detail_pathname4(pathname4 *pathp) +detail_pathname4(pathname4 *pathp, char *what) { char *bp = get_line(0, 0); uint_t component; - sprintf(bp, "File name = "); + sprintf(bp, what); bp += strlen(bp); for (component = 0; component < pathp->pathname4_len; component++) { sprintf(bp, component == 0 ? "%s" : "/%s", - component_name(&pathp->pathname4_val[component])); + component_name(&pathp->pathname4_val[component])); bp += strlen(bp); } } @@ -3571,10 +3568,10 @@ static void detail_rpcsec_gss(rpcsec_gss_info *info) { sprintf(get_line(0, 0), "OID = %s", - tohex(info->oid.sec_oid4_val, info->oid.sec_oid4_len)); + tohex(info->oid.sec_oid4_val, info->oid.sec_oid4_len)); sprintf(get_line(0, 0), "QOP = %u", info->qop); sprintf(get_line(0, 0), "Service = %d (%s)", - info->service, gss_svc_name(info->service)); + info->service, gss_svc_name(info->service)); } /* @@ -3585,7 +3582,7 @@ static void detail_secinfo4(secinfo4 *infop) { sprintf(get_line(0, 0), "Flavor = %d (%s)", - infop->flavor, flavor_name(infop->flavor)); + infop->flavor, flavor_name(infop->flavor)); switch (infop->flavor) { case RPCSEC_GSS: detail_rpcsec_gss(&infop->secinfo4_u.flavor_info); @@ -3608,12 +3605,12 @@ sum_space_limit(nfs_space_limit4 *limitp) switch (limitp->limitby) { case NFS_LIMIT_SIZE: snprintf(buf, buflen, "LB=SZ(%llu)", - limitp->nfs_space_limit4_u.filesize); + limitp->nfs_space_limit4_u.filesize); break; case NFS_LIMIT_BLOCKS: snprintf(buf, buflen, "LB=BL(%u*%u)", - limitp->nfs_space_limit4_u.mod_blocks.num_blocks, - limitp->nfs_space_limit4_u.mod_blocks.bytes_per_block); + limitp->nfs_space_limit4_u.mod_blocks.num_blocks, + limitp->nfs_space_limit4_u.mod_blocks.bytes_per_block); break; default: snprintf(buf, buflen, "LB=?(%d)", limitp->limitby); @@ -3631,19 +3628,19 @@ static void detail_space_limit(nfs_space_limit4 *limitp) { sprintf(get_line(0, 0), "LimitBy = %d (%s)", - limitp->limitby, - limitby_name(limitp->limitby)); + limitp->limitby, + limitby_name(limitp->limitby)); switch (limitp->limitby) { case NFS_LIMIT_SIZE: sprintf(get_line(0, 0), "Bytes = %llu", - limitp->nfs_space_limit4_u.filesize); + limitp->nfs_space_limit4_u.filesize); break; case NFS_LIMIT_BLOCKS: sprintf(get_line(0, 0), "Blocks = %u", - limitp->nfs_space_limit4_u.mod_blocks.num_blocks); + limitp->nfs_space_limit4_u.mod_blocks.num_blocks); sprintf(get_line(0, 0), "Bytes Per Block = %u", - limitp->nfs_space_limit4_u.mod_blocks.bytes_per_block); + limitp->nfs_space_limit4_u.mod_blocks.bytes_per_block); break; } } @@ -3927,8 +3924,8 @@ status_name(int status) case NFS4ERR_RESOURCE: p = "NFS4ERR_RESOURCE"; break; case NFS4ERR_MOVED: p = "NFS4ERR_MOVED"; break; case NFS4ERR_NOFILEHANDLE: p = "NFS4ERR_NOFILEHANDLE"; break; - case NFS4ERR_MINOR_VERS_MISMATCH: p = - "NFS4ERR_MINOR_VERS_MISMATCH"; break; + case NFS4ERR_MINOR_VERS_MISMATCH: p = "NFS4ERR_MINOR_VERS_MISMATCH"; + break; case NFS4ERR_STALE_CLIENTID: p = "NFS4ERR_STALE_CLIENTID"; break; case NFS4ERR_STALE_STATEID: p = "NFS4ERR_STALE_STATEID"; break; case NFS4ERR_OLD_STATEID: p = "NFS4ERR_OLD_STATEID"; break; @@ -4007,7 +4004,7 @@ prt_fh_expire_type(XDR *xdr) sprintf(buf, "Filehandle expire type = "); if ((val & (FH4_NOEXPIRE_WITH_OPEN | FH4_VOLATILE_ANY | - FH4_VOL_MIGRATION | FH4_VOL_RENAME)) == 0) { + FH4_VOL_MIGRATION | FH4_VOL_RENAME)) == 0) { strcat(buf, "Persistent"); return; } @@ -4067,7 +4064,7 @@ prt_link_support(XDR *xdr) if (!xdr_bool(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "Link Support = %s", - val ? "TRUE" : "FALSE"); + val ? "TRUE" : "FALSE"); } static void @@ -4078,7 +4075,7 @@ prt_symlink_support(XDR *xdr) if (!xdr_bool(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "Symlink Support = %s", - val ? "TRUE" : "FALSE"); + val ? "TRUE" : "FALSE"); } static void @@ -4089,7 +4086,7 @@ prt_named_attr(XDR *xdr) if (!xdr_bool(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "Has Named Attributes = %s", - val ? "TRUE" : "FALSE"); + val ? "TRUE" : "FALSE"); } static void @@ -4100,7 +4097,7 @@ prt_fsid(XDR *xdr) if (!xdr_fsid4(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "FS ID: Major = %llx, Minor = %llx", - val.major, val.minor); + val.major, val.minor); } static void @@ -4111,7 +4108,7 @@ prt_unique_handles(XDR *xdr) if (!xdr_bool(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "Unique Handles = %s", - val ? "TRUE" : "FALSE"); + val ? "TRUE" : "FALSE"); } static void @@ -4132,7 +4129,7 @@ prt_rdattr_error(XDR *xdr) if (!xdr_nfsstat4(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "Rdattr Error = %u (%s)", - val, status_name(val)); + val, status_name(val)); } static void @@ -4253,7 +4250,7 @@ prt_archive(XDR *xdr) if (!xdr_bool(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "Archived = %s", - val ? "TRUE" : "FALSE"); + val ? "TRUE" : "FALSE"); } static void @@ -4264,7 +4261,7 @@ prt_cansettime(XDR *xdr) if (!xdr_bool(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "Server Can Set Time = %s", - val ? "TRUE" : "FALSE"); + val ? "TRUE" : "FALSE"); } static void @@ -4275,7 +4272,7 @@ prt_case_insensitive(XDR *xdr) if (!xdr_bool(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "Case Insensitive Lookups = %s", - val ? "TRUE" : "FALSE"); + val ? "TRUE" : "FALSE"); } static void @@ -4286,7 +4283,7 @@ prt_case_preserving(XDR *xdr) if (!xdr_bool(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "Case Preserving = %s", - val ? "TRUE" : "FALSE"); + val ? "TRUE" : "FALSE"); } static void @@ -4297,7 +4294,7 @@ prt_chown_restricted(XDR *xdr) if (!xdr_bool(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "Chown Is Restricted = %s", - val ? "TRUE" : "FALSE"); + val ? "TRUE" : "FALSE"); } static void @@ -4362,14 +4359,29 @@ prt_files_total(XDR *xdr) } static void +prt_fs_location(fs_location4 *fsl) +{ + int i; + + for (i = 0; i < fsl->server.server_len; i++) + sprintf(get_line(0, 0), "server: %s", + utf8localize(&fsl->server.server_val[i])); + + detail_pathname4(&fsl->rootpath, "rootpath: "); +} + +static void prt_fs_locations(XDR *xdr) { static fs_locations4 val; + int i; if (!xdr_fs_locations4(xdr, &val)) longjmp(xdr_err, 1); - sprintf(get_line(0, 0), "[fs_locations]"); /* XXX */ - detail_pathname4(&val.fs_root); + sprintf(get_line(0, 0), "[fs_locations]"); + detail_pathname4(&val.fs_root, "fs_root: "); + for (i = 0; i < val.locations.locations_len; i++) + prt_fs_location(&val.locations.locations_val[i]); xdr_free(xdr_fs_locations4, (char *)&val); } @@ -4381,7 +4393,7 @@ prt_hidden(XDR *xdr) if (!xdr_bool(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "Hidden = %s", - val ? "TRUE" : "FALSE"); + val ? "TRUE" : "FALSE"); } static void @@ -4392,7 +4404,7 @@ prt_homogeneous(XDR *xdr) if (!xdr_bool(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "FS Is Homogeneous = %s", - val ? "TRUE" : "FALSE"); + val ? "TRUE" : "FALSE"); } static void @@ -4475,7 +4487,7 @@ prt_no_trunc(XDR *xdr) if (!xdr_bool(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "Long Names Are Error (no_trunc) = %s", - val ? "TRUE" : "FALSE"); + val ? "TRUE" : "FALSE"); } static void @@ -4548,7 +4560,7 @@ prt_rawdev(XDR *xdr) if (!xdr_specdata4(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "Raw Device ID = %u, %u", - val.specdata1, val.specdata2); + val.specdata1, val.specdata2); } static void @@ -4599,7 +4611,7 @@ prt_system(XDR *xdr) if (!xdr_bool(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "System File = %s", - val ? "TRUE" : "FALSE"); + val ? "TRUE" : "FALSE"); } static void @@ -4610,7 +4622,7 @@ prt_time_access(XDR *xdr) if (!xdr_nfstime4(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "Last Access Time = %s", - format_time(val.seconds, val.nseconds)); + format_time(val.seconds, val.nseconds)); } static void @@ -4622,8 +4634,8 @@ prt_time_access_set(XDR *xdr) longjmp(xdr_err, 1); if (val.set_it == SET_TO_CLIENT_TIME4) { sprintf(get_line(0, 0), "Access Time = %s (set to client time)", - format_time(val.settime4_u.time.seconds, - val.settime4_u.time.nseconds)); + format_time(val.settime4_u.time.seconds, + val.settime4_u.time.nseconds)); } else if (val.set_it == SET_TO_SERVER_TIME4) { sprintf(get_line(0, 0), "Access Time (set to server time)"); } else @@ -4638,7 +4650,7 @@ prt_time_backup(XDR *xdr) if (!xdr_nfstime4(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "Last Backup Time = %s", - format_time(val.seconds, val.nseconds)); + format_time(val.seconds, val.nseconds)); } static void @@ -4649,7 +4661,7 @@ prt_time_create(XDR *xdr) if (!xdr_nfstime4(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "Creation Time = %s", - format_time(val.seconds, val.nseconds)); + format_time(val.seconds, val.nseconds)); } static void @@ -4660,7 +4672,7 @@ prt_time_delta(XDR *xdr) if (!xdr_nfstime4(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "Server Time Granularity = %lld.%09d sec", - val.seconds, val.nseconds); + val.seconds, val.nseconds); } static void @@ -4671,7 +4683,7 @@ prt_time_metadata(XDR *xdr) if (!xdr_nfstime4(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "Last Metadata Change Time = %s", - format_time(val.seconds, val.nseconds)); + format_time(val.seconds, val.nseconds)); } static void @@ -4682,7 +4694,7 @@ prt_time_modify(XDR *xdr) if (!xdr_nfstime4(xdr, &val)) longjmp(xdr_err, 1); sprintf(get_line(0, 0), "Last Modification Time = %s", - format_time(val.seconds, val.nseconds)); + format_time(val.seconds, val.nseconds)); } static void @@ -4694,12 +4706,12 @@ prt_time_modify_set(XDR *xdr) longjmp(xdr_err, 1); if (val.set_it == SET_TO_CLIENT_TIME4) { sprintf(get_line(0, 0), - "Modification Time = %s (set to client time)", - format_time(val.settime4_u.time.seconds, - val.settime4_u.time.nseconds)); + "Modification Time = %s (set to client time)", + format_time(val.settime4_u.time.seconds, + val.settime4_u.time.nseconds)); } else if (val.set_it == SET_TO_SERVER_TIME4) { sprintf(get_line(0, 0), - "Modification Time (set to server time)"); + "Modification Time (set to server time)"); } else longjmp(xdr_err, 1); } @@ -4760,7 +4772,7 @@ utf8localize(utf8string *utf8str) } if (newsize != oldsize) { utf_buf[cur_utf_buf] = realloc(utf_buf[cur_utf_buf], - newsize); + newsize); if (utf_buf[cur_utf_buf] == NULL) { pr_err("out of memory\n"); utf_buflen[cur_utf_buf] = 0; diff --git a/usr/src/cmd/fs.d/autofs/Makefile b/usr/src/cmd/fs.d/autofs/Makefile index c5cc33e46a..71021cbce5 100644 --- a/usr/src/cmd/fs.d/autofs/Makefile +++ b/usr/src/cmd/fs.d/autofs/Makefile @@ -71,8 +71,8 @@ LOCAL= autod_main.o \ autod_parse.o autod_mount.o autod_nfs.o nfs_cast.o \ autod_autofs.o autod_xdr.o autod_readdir.o autod_lookup.o -TYPEOBJS= $(LOCAL) $(COMMON) replica.o nfs_sec.o nfs_subr.o $(FSLIB) \ - webnfs_xdr.o webnfs_client.o selfcheck.o +TYPEOBJS= $(LOCAL) $(COMMON) replica.o nfs_sec.o nfs_resolve.o nfs_subr.o \ + $(FSLIB) webnfs_xdr.o webnfs_client.o selfcheck.o SHAREOBJS= $(SHARESRCS:%.c=%.o) @@ -87,7 +87,7 @@ $(MOUNT):= LDLIBS += -lnsl $(TYPEPROG) := LDLIBS += -lrpcsvc -lsocket -lnsl -lsldap -lkstat CFLAGS += $(CCVERBOSE) -D_FILE_OFFSET_BITS=64 -CPPFLAGS= -I. -I.. -I../nfs/lib $(CPPFLAGS.master) -D_REENTRANT \ +CPPFLAGS= -I. -I.. -I../nfs/lib $(CPPFLAGS.master) -D_REENTRANT \ $(MALLOC_DEBUG) OBJS= $(AUTOOBJS) $(MOUNTOBJS) $(TYPEOBJS) \ $(SHAREOBJS) $(UNSHAREOBJS) @@ -96,7 +96,8 @@ AUTOSRCS= automount.c $(COMMON:%.o=%.c) $(FSLIBSRC) MOUNTSRCS= mount.c $(FSLIBSRC) TYPESRCS= $(LOCAL:%.o=%.c) $(COMMON:%.o=%.c) \ ../nfs/lib/replica.c ../nfs/lib/nfs_sec.c \ - ../nfs/lib/nfs_subr.c $(FSLIBSRC) ../nfs/lib/selfcheck.c + ../nfs/lib/nfs_subr.c $(FSLIBSRC) ../nfs/lib/selfcheck.c \ + ../nfs/lib/nfs_resolve.c SHARESRCS= $(SHARE:%=%.c) UNSHARESRCS= $(UNSHARE:%=%.c) DFSHARESSRCS= $(DFSHARES:%=%.sh) @@ -168,6 +169,8 @@ nfs_subr.o: ../nfs/lib/nfs_subr.c selfcheck.o: ../nfs/lib/selfcheck.c $(COMPILE.c) ../nfs/lib/selfcheck.c +nfs_resolve.o: ../nfs/lib/nfs_resolve.c + $(COMPILE.c) ../nfs/lib/nfs_resolve.c webnfs_xdr.c: webnfs.x $(RPCGEN) -M -C -c -o $@ webnfs.x diff --git a/usr/src/cmd/fs.d/autofs/autod_nfs.c b/usr/src/cmd/fs.d/autofs/autod_nfs.c index 89f278b904..32f96245ad 100644 --- a/usr/src/cmd/fs.d/autofs/autod_nfs.c +++ b/usr/src/cmd/fs.d/autofs/autod_nfs.c @@ -74,9 +74,9 @@ #include "replica.h" #include "nfs_subr.h" #include "webnfs.h" +#include "nfs_resolve.h" #include <sys/sockio.h> #include <net/if.h> -#include <assert.h> #include <rpcsvc/daemon_utils.h> #include <pwd.h> #include <strings.h> @@ -91,11 +91,6 @@ extern enum snego_stat nfs_sec_nego(); #define MAXHOSTS 512 -/* number of transports to try */ -#define MNT_PREF_LISTLEN 2 -#define FIRST_TRY 1 -#define SECOND_TRY 2 - #define MNTTYPE_CACHEFS "cachefs" /* @@ -134,9 +129,7 @@ static enum nfsstat nfsmount(struct mapfs *, char *, char *, int, int, uid_t, action_list *); static int is_nfs_port(char *); -void netbuf_free(struct netbuf *); -struct knetconfig *get_knconf(struct netconfig *); -void free_knconf(struct knetconfig *); +static void netbuf_free(struct netbuf *); static int get_pathconf(CLIENT *, char *, char *, struct pathcnf **, int); static struct mapfs *enum_servers(struct mapent *, char *); static struct mapfs *get_mysubnet_servers(struct mapfs *); @@ -156,12 +149,18 @@ enum type_of_stuff { SERVER_FH = 2 }; -void *get_server_stuff(enum type_of_stuff, char *, rpcprog_t, +static void *get_server_netinfo(enum type_of_stuff, char *, rpcprog_t, rpcvers_t, mfs_snego_t *, struct netconfig **, char *, ushort_t, struct t_info *, caddr_t *, bool_t, char *, enum clnt_stat *); - -void *get_the_stuff(enum type_of_stuff, char *, rpcprog_t, - rpcvers_t, mfs_snego_t *, struct netconfig *, ushort_t, struct t_info *, +static void *get_netconfig_info(enum type_of_stuff, char *, rpcprog_t, + rpcvers_t, struct netconfig *, ushort_t, struct t_info *, + struct t_bind *, caddr_t *, bool_t, char *, enum clnt_stat *, + mfs_snego_t *); +static void *get_server_addrorping(char *, rpcprog_t, rpcvers_t, + struct netconfig *, ushort_t, struct t_info *, struct t_bind *, + caddr_t *, bool_t, char *, enum clnt_stat *, int); +static void *get_server_fh(char *, rpcprog_t, rpcvers_t, mfs_snego_t *, + struct netconfig *, ushort_t, struct t_info *, struct t_bind *, caddr_t *, bool_t, char *, enum clnt_stat *); struct mapfs *add_mfs(struct mapfs *, int, struct mapfs **, struct mapfs **); @@ -1877,13 +1876,14 @@ try_mnt_slash: /* * For NFSv4, we want to avoid rpcbind, so call - * get_server_stuff() directly to tell it that + * get_server_netinfo() directly to tell it that * we want to go "direct_to_server". Otherwise, * do what has always been done. */ if (nfsvers == NFS_V4) { enum clnt_stat cstat; - argp->addr = get_server_stuff(SERVER_ADDR, + + argp->addr = get_server_netinfo(SERVER_ADDR, host, NFS_PROGRAM, nfsvers, NULL, &nconf, nfs_proto, thisport, NULL, NULL, TRUE, NULL, &cstat); @@ -2010,7 +2010,7 @@ try_mnt_slash: * * Eventurally, we want to move this code to nfs_clnt_secdata() * when autod_nfs.c and mount.c can share the same - * get_the_addr/get_the_stuff routine. + * get_the_addr/get_netconfig_info routine. */ secflags = 0; syncaddr = NULL; @@ -2024,9 +2024,10 @@ try_mnt_slash: */ if ((mfs->mfs_flags & MFS_FH_VIA_WEBNFS) == 0 && nfsvers != NFS_V4) { - syncaddr = get_the_stuff(SERVER_ADDR, host, RPCBPROG, - RPCBVERS, NULL, nconf, 0, NULL, NULL, FALSE, - NULL, NULL); + enum clnt_stat cstat; + syncaddr = get_server_netinfo(SERVER_ADDR, + host, RPCBPROG, RPCBVERS, NULL, &nconf, + NULL, 0, NULL, NULL, FALSE, NULL, &cstat); } if (syncaddr != NULL) { @@ -2527,50 +2528,6 @@ get_pathconf(CLIENT *cl, char *path, char *fsname, struct pathcnf **pcnf, return (RET_OK); } -struct knetconfig * -get_knconf(nconf) - struct netconfig *nconf; -{ - struct stat stbuf; - struct knetconfig *k; - - if (stat(nconf->nc_device, &stbuf) < 0) { - syslog(LOG_ERR, "get_knconf: stat %s: %m", nconf->nc_device); - return (NULL); - } - k = (struct knetconfig *)malloc(sizeof (*k)); - if (k == NULL) - goto nomem; - k->knc_semantics = nconf->nc_semantics; - k->knc_protofmly = strdup(nconf->nc_protofmly); - if (k->knc_protofmly == NULL) - goto nomem; - k->knc_proto = strdup(nconf->nc_proto); - if (k->knc_proto == NULL) - goto nomem; - k->knc_rdev = stbuf.st_rdev; - - return (k); - -nomem: - syslog(LOG_ERR, "get_knconf: no memory"); - free_knconf(k); - return (NULL); -} - -void -free_knconf(k) - struct knetconfig *k; -{ - if (k == NULL) - return; - if (k->knc_protofmly) - free(k->knc_protofmly); - if (k->knc_proto) - free(k->knc_proto); - free(k); -} - void netbuf_free(nb) struct netbuf *nb; @@ -2833,393 +2790,6 @@ get_cached_srv_addr(char *hostname, rpcprog_t prog, rpcvers_t vers, } /* - * Get the network address on "hostname" for program "prog" - * with version "vers" by using the nconf configuration data - * passed in. - * - * If the address of a netconfig pointer is null then - * information is not sufficient and no netbuf will be returned. - * - * tinfo argument is for matching the get_the_addr() defined in - * ../nfs/mount/mount.c - */ -void * -get_the_stuff( - enum type_of_stuff type_of_stuff, - char *hostname, - rpcprog_t prog, - rpcprog_t vers, - mfs_snego_t *mfssnego, - struct netconfig *nconf, - ushort_t port, - struct t_info *tinfo, - caddr_t *fhp, - bool_t direct_to_server, - char *fspath, - enum clnt_stat *cstat) - -{ - struct netbuf *nb = NULL; - struct t_bind *tbind = NULL; - int fd = -1; - enum clnt_stat cs = RPC_TIMEDOUT; - CLIENT *cl = NULL; - struct timeval tv; - AUTH *ah = NULL; - AUTH *new_ah = NULL; - struct snego_t snego; - - if (nconf == NULL) { - goto done; - } - - if (prog == NFS_PROGRAM && vers == NFS_V4) - if (strncasecmp(nconf->nc_proto, NC_UDP, strlen(NC_UDP)) == 0) - goto done; - - if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) < 0) { - goto done; - } - - /* LINTED pointer alignment */ - if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR)) - == NULL) { - goto done; - } - - if (direct_to_server == TRUE) { - struct nd_hostserv hs; - struct nd_addrlist *retaddrs; - hs.h_host = hostname; - - if (trace > 1) - trace_prt(1, " get_the_stuff: %s call " - "direct to server %s\n", - type_of_stuff == SERVER_FH ? "pub fh" : - type_of_stuff == SERVER_ADDR ? "get address" : - type_of_stuff == SERVER_PING ? "ping" : - "unknown", hostname); - if (port == 0) - hs.h_serv = "nfs"; - else - hs.h_serv = NULL; - - if (netdir_getbyname(nconf, &hs, &retaddrs) != ND_OK) { - goto done; - } - memcpy(tbind->addr.buf, retaddrs->n_addrs->buf, - retaddrs->n_addrs->len); - tbind->addr.len = retaddrs->n_addrs->len; - netdir_free((void *)retaddrs, ND_ADDRLIST); - if (port) { - /* LINTED pointer alignment */ - - if (strcmp(nconf->nc_protofmly, NC_INET) == NULL) - ((struct sockaddr_in *) - tbind->addr.buf)->sin_port = - htons((ushort_t)port); - else if (strcmp(nconf->nc_protofmly, NC_INET6) == NULL) - ((struct sockaddr_in6 *) - tbind->addr.buf)->sin6_port = - htons((ushort_t)port); - } - - if (type_of_stuff == SERVER_FH) { - if (netdir_options(nconf, ND_SET_RESERVEDPORT, fd, - NULL) == -1) - if (trace > 1) - trace_prt(1, "\tget_the_stuff: " - "ND_SET_RESERVEDPORT(%s) " - "failed\n", hostname); - } - - cl = clnt_tli_create(fd, nconf, &tbind->addr, prog, - vers, 0, 0); - - if (trace > 1) - trace_prt(1, " get_the_stuff: clnt_tli_create(%s) " - "returned %p\n", hostname, cl); - if (cl == NULL) - goto done; -#ifdef MALLOC_DEBUG - add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__); - add_alloc("AUTH_HANDLE", cl->cl_auth, 0, - __FILE__, __LINE__); -#endif - - switch (type_of_stuff) { - case SERVER_FH: - { - enum snego_stat sec; - - ah = authsys_create_default(); - if (ah != NULL) { -#ifdef MALLOC_DEBUG - drop_alloc("AUTH_HANDLE", cl->cl_auth, - __FILE__, __LINE__); -#endif - AUTH_DESTROY(cl->cl_auth); - cl->cl_auth = ah; -#ifdef MALLOC_DEBUG - add_alloc("AUTH_HANDLE", cl->cl_auth, 0, - __FILE__, __LINE__); -#endif - } - - if (!mfssnego->snego_done && vers != NFS_V4) { - /* - * negotiate sec flavor. - */ - snego.cnt = 0; - if ((sec = nfs_sec_nego(vers, cl, fspath, &snego)) == - SNEGO_SUCCESS) { - int jj; - - /* - * check if server supports the one - * specified in the sec= option. - */ - if (mfssnego->sec_opt) { - for (jj = 0; jj < snego.cnt; jj++) { - if (snego.array[jj] == - mfssnego->nfs_sec.sc_nfsnum) { - mfssnego->snego_done = TRUE; - break; - } - } - } - - /* - * find a common sec flavor - */ - if (!mfssnego->snego_done) { - for (jj = 0; jj < snego.cnt; jj++) { - if (!nfs_getseconfig_bynumber( - snego.array[jj], &mfssnego->nfs_sec)) { - mfssnego->snego_done = TRUE; - break; - } - } - } - if (!mfssnego->snego_done) - return (NULL); - - /* - * Now that the flavor has been - * negotiated, get the fh. - * - * First, create an auth handle using the negotiated - * sec flavor in the next lookup to - * fetch the filehandle. - */ - new_ah = nfs_create_ah(cl, hostname, - &mfssnego->nfs_sec); - if (new_ah == NULL) - goto done; -#ifdef MALLOC_DEBUG - drop_alloc("AUTH_HANDLE", cl->cl_auth, - __FILE__, __LINE__); -#endif - AUTH_DESTROY(cl->cl_auth); - cl->cl_auth = new_ah; -#ifdef MALLOC_DEBUG - add_alloc("AUTH_HANDLE", cl->cl_auth, 0, - __FILE__, __LINE__); -#endif - } else if (sec == SNEGO_ARRAY_TOO_SMALL || - sec == SNEGO_FAILURE) { - goto done; - } - /* - * Note that if sec == SNEGO_DEF_VALID - * the default sec flavor is acceptable. - * Use it to get the filehandle. - */ - } - } - - switch (vers) { - case NFS_VERSION: - { - wnl_diropargs arg; - wnl_diropres res; - - memset((char *)&arg.dir, 0, sizeof (wnl_fh)); - memset((char *)&res, 0, sizeof (wnl_diropres)); - arg.name = fspath; - if (wnlproc_lookup_2(&arg, &res, cl) != - RPC_SUCCESS || res.status != NFS_OK) - goto done; - *fhp = malloc(sizeof (wnl_fh)); - - if (*fhp == NULL) { - syslog(LOG_ERR, "no memory\n"); - goto done; - } - - memcpy((char *)*fhp, - (char *)&res.wnl_diropres_u.wnl_diropres.file, - sizeof (wnl_fh)); - cs = RPC_SUCCESS; - } - break; - case NFS_V3: - { - WNL_LOOKUP3args arg; - WNL_LOOKUP3res res; - nfs_fh3 *fh3p; - - memset((char *)&arg.what.dir, 0, sizeof (wnl_fh3)); - memset((char *)&res, 0, sizeof (WNL_LOOKUP3res)); - arg.what.name = fspath; - if (wnlproc3_lookup_3(&arg, &res, cl) != - RPC_SUCCESS || res.status != NFS3_OK) - goto done; - - fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p)); - - if (fh3p == NULL) { - syslog(LOG_ERR, "no memory\n"); - goto done; - } - - fh3p->fh3_length = res. - WNL_LOOKUP3res_u.res_ok.object.data.data_len; - memcpy(fh3p->fh3_u.data, res. - WNL_LOOKUP3res_u.res_ok.object.data.data_val, - fh3p->fh3_length); - - *fhp = (caddr_t)fh3p; - - cs = RPC_SUCCESS; - } - break; - case NFS_V4: - tv.tv_sec = 10; - tv.tv_usec = 0; - cs = clnt_call(cl, NULLPROC, xdr_void, 0, - xdr_void, 0, tv); - if (cs != RPC_SUCCESS) - goto done; - *fhp = strdup(fspath); - break; - } - break; - case SERVER_ADDR: - case SERVER_PING: - tv.tv_sec = 10; - tv.tv_usec = 0; - cs = clnt_call(cl, NULLPROC, xdr_void, 0, - xdr_void, 0, tv); - if (trace > 1) - trace_prt(1, - "get_the_stuff: clnt_call(%s) " - "returned %s\n", - hostname, - cs == RPC_SUCCESS ? "success" : - "failure"); - - if (cs != RPC_SUCCESS) - goto done; - break; - } - - } else if (type_of_stuff != SERVER_FH) { - - if (type_of_stuff == SERVER_ADDR) { - if (get_cached_srv_addr(hostname, prog, vers, nconf, - &tbind->addr) == 0) - goto done; - } - - if (port) { - /* LINTED pointer alignment */ - if (strcmp(nconf->nc_protofmly, NC_INET) == NULL) - ((struct sockaddr_in *) - tbind->addr.buf)->sin_port = - htons((ushort_t)port); - else if (strcmp(nconf->nc_protofmly, NC_INET6) == NULL) - ((struct sockaddr_in6 *) - tbind->addr.buf)->sin6_port = - htons((ushort_t)port); - cl = clnt_tli_create(fd, nconf, &tbind->addr, - prog, vers, 0, 0); - if (cl == NULL) - goto done; -#ifdef MALLOC_DEBUG - add_alloc("CLNT_HANDLE", cl, 0, __FILE__, __LINE__); - add_alloc("AUTH_HANDLE", cl->cl_auth, 0, - __FILE__, __LINE__); -#endif - tv.tv_sec = 10; - tv.tv_usec = 0; - cs = clnt_call(cl, NULLPROC, xdr_void, 0, xdr_void, - 0, tv); - if (cs != RPC_SUCCESS) - goto done; - } - - } else { - /* can't happen */ - goto done; - } - - if (type_of_stuff != SERVER_PING) { - - cs = RPC_SYSTEMERROR; - - /* - * Make a copy of the netbuf to return - */ - nb = (struct netbuf *)malloc(sizeof (struct netbuf)); - if (nb == NULL) { - syslog(LOG_ERR, "no memory\n"); - goto done; - } - *nb = tbind->addr; - nb->buf = (char *)malloc(nb->maxlen); - if (nb->buf == NULL) { - syslog(LOG_ERR, "no memory\n"); - free(nb); - nb = NULL; - goto done; - } - (void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len); - - cs = RPC_SUCCESS; - } - -done: - if (cl != NULL) { - if (ah != NULL) { -#ifdef MALLOC_DEBUG - drop_alloc("AUTH_HANDLE", cl->cl_auth, - __FILE__, __LINE__); -#endif - AUTH_DESTROY(cl->cl_auth); - cl->cl_auth = NULL; - } -#ifdef MALLOC_DEBUG - drop_alloc("CLNT_HANDLE", cl, __FILE__, __LINE__); -#endif - clnt_destroy(cl); - } - - if (tbind) { - t_free((char *)tbind, T_BIND); - tbind = NULL; - } - - if (fd >= 0) - (void) t_close(fd); - - if (cstat != NULL) - *cstat = cs; - - return (nb); -} - -/* * Get a network address on "hostname" for program "prog" * with version "vers". If the port number is specified (non zero) * then try for a TCP/UDP transport and set the port number of the @@ -3242,7 +2812,7 @@ get_addr(char *hostname, rpcprog_t prog, rpcvers_t vers, { enum clnt_stat cstat; - return (get_server_stuff(SERVER_ADDR, hostname, prog, vers, NULL, + return (get_server_netinfo(SERVER_ADDR, hostname, prog, vers, NULL, nconfp, proto, port, tinfo, NULL, FALSE, NULL, &cstat)); } @@ -3253,7 +2823,7 @@ get_pubfh(char *hostname, rpcvers_t vers, mfs_snego_t *mfssnego, { enum clnt_stat cstat; - return (get_server_stuff(SERVER_FH, hostname, NFS_PROGRAM, vers, + return (get_server_netinfo(SERVER_FH, hostname, NFS_PROGRAM, vers, mfssnego, nconfp, proto, port, tinfo, fhp, get_pubfh, fspath, &cstat)); } @@ -3264,14 +2834,15 @@ get_ping(char *hostname, rpcprog_t prog, rpcvers_t vers, { enum clnt_stat cstat; - (void) get_server_stuff(SERVER_PING, hostname, prog, vers, NULL, nconfp, - NULL, port, NULL, NULL, direct_to_server, NULL, &cstat); + (void) get_server_netinfo(SERVER_PING, hostname, prog, + vers, NULL, nconfp, NULL, port, NULL, NULL, + direct_to_server, NULL, &cstat); return (cstat); } void * -get_server_stuff( +get_server_netinfo( enum type_of_stuff type_of_stuff, char *hostname, rpcprog_t prog, @@ -3289,13 +2860,16 @@ get_server_stuff( struct netbuf *nb = NULL; struct netconfig *nconf = NULL; NCONF_HANDLE *nc = NULL; + int error = 0; + int fd = 0; + struct t_bind *tbind = NULL; int nthtry = FIRST_TRY; - if (nconfp && *nconfp) - return (get_the_stuff(type_of_stuff, hostname, prog, vers, - mfssnego, *nconfp, port, tinfo, fhp, direct_to_server, - fspath, cstatp)); - + if (nconfp && *nconfp) { + return (get_netconfig_info(type_of_stuff, hostname, + prog, vers, nconf, port, tinfo, tbind, fhp, + direct_to_server, fspath, cstatp, mfssnego)); + } /* * No nconf passed in. @@ -3314,8 +2888,7 @@ get_server_stuff( * otherwise try COTS first, if failed, then try CLTS. */ if (proto) { - - while (nconf = getnetpath(nc)) { + while ((nconf = getnetpath(nc)) != NULL) { if (strcmp(nconf->nc_proto, proto)) continue; /* @@ -3329,24 +2902,20 @@ get_server_stuff( strcmp(nconf->nc_proto, NC_UDP))) continue; } - - nb = get_the_stuff(type_of_stuff, hostname, prog, vers, - mfssnego, nconf, port, tinfo, fhp, - direct_to_server, fspath, cstatp); - + nb = get_netconfig_info(type_of_stuff, hostname, + prog, vers, nconf, port, tinfo, tbind, fhp, + direct_to_server, fspath, cstatp, mfssnego); if (*cstatp == RPC_SUCCESS) break; assert(nb == NULL); - } /* end of while */ - + } if (nconf == NULL) goto done; - } else { retry: - while (nconf = getnetpath(nc)) { + while ((nconf = getnetpath(nc)) != NULL) { if (nconf->nc_flag & NC_VISIBLE) { if (nthtry == FIRST_TRY) { if ((nconf->nc_semantics == @@ -3379,7 +2948,8 @@ retry: } } } - } /* while */ + } + if (nconf == NULL) { if (++nthtry <= MNT_PREF_LISTLEN) { endnetpath(nc); @@ -3389,9 +2959,9 @@ retry: } else goto done; } else { - nb = get_the_stuff(type_of_stuff, hostname, prog, vers, - mfssnego, nconf, port, tinfo, fhp, direct_to_server, - fspath, cstatp); + nb = get_netconfig_info(type_of_stuff, hostname, + prog, vers, nconf, port, tinfo, tbind, fhp, + direct_to_server, fspath, cstatp, mfssnego); if (*cstatp != RPC_SUCCESS) /* * Continue the same search path in the @@ -3400,17 +2970,20 @@ retry: */ goto retry; } - } /* if !proto */ + } /* * Got nconf and nb. Now dup the netconfig structure (nconf) * and return it thru nconfp. */ - *nconfp = getnetconfigent(nconf->nc_netid); - if (*nconfp == NULL) { - syslog(LOG_ERR, "no memory\n"); - free(nb); - nb = NULL; + if (nconf != NULL) { + if ((*nconfp = getnetconfigent(nconf->nc_netid)) == NULL) { + syslog(LOG_ERR, "no memory\n"); + free(nb); + nb = NULL; + } + } else { + *nconfp = NULL; } done: if (nc) @@ -3418,6 +2991,221 @@ done: return (nb); } +void * +get_server_fh(char *hostname, rpcprog_t prog, rpcvers_t vers, + mfs_snego_t *mfssnego, struct netconfig *nconf, ushort_t port, + struct t_info *tinfo, struct t_bind *tbind, caddr_t *fhp, + bool_t direct_to_server, char *fspath, enum clnt_stat *cstat) +{ + AUTH *ah = NULL; + AUTH *new_ah = NULL; + struct snego_t snego; + enum clnt_stat cs = RPC_TIMEDOUT; + struct timeval tv; + bool_t file_handle = 1; + enum snego_stat sec; + CLIENT *cl = NULL; + int fd = -1; + struct netbuf *nb = NULL; + + if (direct_to_server != TRUE) + return (NULL); + + if (prog == NFS_PROGRAM && vers == NFS_V4) + if (strncasecmp(nconf->nc_proto, NC_UDP, strlen(NC_UDP)) == 0) + goto done; + + if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) < 0) + goto done; + + /* LINTED pointer alignment */ + if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR)) == NULL) + goto done; + + if (setup_nb_parms(nconf, tbind, tinfo, hostname, fd, + direct_to_server, port, prog, vers, file_handle) < 0) { + goto done; + } + + cl = clnt_tli_create(fd, nconf, &tbind->addr, prog, vers, 0, 0); + if (cl == NULL) + goto done; + + ah = authsys_create_default(); + if (ah != NULL) { +#ifdef MALLOC_DEBUG + drop_alloc("AUTH_HANDLE", cl->cl_auth, + __FILE__, __LINE__); +#endif + AUTH_DESTROY(cl->cl_auth); + cl->cl_auth = ah; +#ifdef MALLOC_DEBUG + add_alloc("AUTH_HANDLE", cl->cl_auth, 0, + __FILE__, __LINE__); +#endif + } + + if (!mfssnego->snego_done && vers != NFS_V4) { + /* + * negotiate sec flavor. + */ + snego.cnt = 0; + if ((sec = nfs_sec_nego(vers, cl, fspath, &snego)) == + SNEGO_SUCCESS) { + int jj; + + /* + * check if server supports the one + * specified in the sec= option. + */ + if (mfssnego->sec_opt) { + for (jj = 0; jj < snego.cnt; jj++) { + if (snego.array[jj] == + mfssnego->nfs_sec.sc_nfsnum) { + mfssnego->snego_done = TRUE; + break; + } + } + } + + /* + * find a common sec flavor + */ + if (!mfssnego->snego_done) { + for (jj = 0; jj < snego.cnt; jj++) { + if (!nfs_getseconfig_bynumber( + snego.array[jj], + &mfssnego->nfs_sec)) { + mfssnego->snego_done = TRUE; + break; + } + } + } + if (!mfssnego->snego_done) + goto done; + /* + * Now that the flavor has been + * negotiated, get the fh. + * + * First, create an auth handle using the negotiated + * sec flavor in the next lookup to + * fetch the filehandle. + */ + new_ah = nfs_create_ah(cl, hostname, + &mfssnego->nfs_sec); + if (new_ah == NULL) + goto done; +#ifdef MALLOC_DEBUG + drop_alloc("AUTH_HANDLE", cl->cl_auth, + __FILE__, __LINE__); +#endif + AUTH_DESTROY(cl->cl_auth); + cl->cl_auth = new_ah; +#ifdef MALLOC_DEBUG + add_alloc("AUTH_HANDLE", cl->cl_auth, 0, + __FILE__, __LINE__); +#endif + } else if (sec == SNEGO_ARRAY_TOO_SMALL || + sec == SNEGO_FAILURE) { + goto done; + } + } + + switch (vers) { + case NFS_VERSION: + { + wnl_diropargs arg; + wnl_diropres res; + + memset((char *)&arg.dir, 0, sizeof (wnl_fh)); + memset((char *)&res, 0, sizeof (wnl_diropres)); + arg.name = fspath; + if (wnlproc_lookup_2(&arg, &res, cl) != + RPC_SUCCESS || res.status != NFS_OK) + goto done; + *fhp = malloc(sizeof (wnl_fh)); + + if (*fhp == NULL) { + syslog(LOG_ERR, "no memory\n"); + goto done; + } + + memcpy((char *)*fhp, + (char *)&res.wnl_diropres_u.wnl_diropres.file, + sizeof (wnl_fh)); + cs = RPC_SUCCESS; + } + break; + case NFS_V3: + { + WNL_LOOKUP3args arg; + WNL_LOOKUP3res res; + nfs_fh3 *fh3p; + + memset((char *)&arg.what.dir, 0, sizeof (wnl_fh3)); + memset((char *)&res, 0, sizeof (WNL_LOOKUP3res)); + arg.what.name = fspath; + if (wnlproc3_lookup_3(&arg, &res, cl) != + RPC_SUCCESS || res.status != NFS3_OK) + goto done; + + fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p)); + + if (fh3p == NULL) { + syslog(LOG_ERR, "no memory\n"); + goto done; + } + + fh3p->fh3_length = + res.WNL_LOOKUP3res_u.res_ok.object.data.data_len; + memcpy(fh3p->fh3_u.data, + res.WNL_LOOKUP3res_u.res_ok.object.data.data_val, + fh3p->fh3_length); + + *fhp = (caddr_t)fh3p; + + cs = RPC_SUCCESS; + } + break; + case NFS_V4: + tv.tv_sec = 10; + tv.tv_usec = 0; + cs = clnt_call(cl, NULLPROC, xdr_void, 0, + xdr_void, 0, tv); + if (cs != RPC_SUCCESS) + goto done; + + *fhp = strdup(fspath); + if (fhp == NULL) { + cs = RPC_SYSTEMERROR; + goto done; + } + break; + } + nb = (struct netbuf *)malloc(sizeof (struct netbuf)); + if (nb == NULL) { + syslog(LOG_ERR, "no memory\n"); + cs = RPC_SYSTEMERROR; + goto done; + } + nb->buf = (char *)malloc(tbind->addr.maxlen); + if (nb->buf == NULL) { + syslog(LOG_ERR, "no memory\n"); + free(nb); + nb = NULL; + cs = RPC_SYSTEMERROR; + goto done; + } + (void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len); + nb->len = tbind->addr.len; + nb->maxlen = tbind->addr.maxlen; +done: + if (cstat != NULL) + *cstat = cs; + destroy_auth_client_handle(cl); + cleanup_tli_parms(tbind, fd); + return (nb); +} /* * Sends a null call to the remote host's (NFS program, versp). versp @@ -4535,3 +4323,121 @@ free_nfs_args(struct nfs_args *argp) free(oldp); } } + +void * +get_netconfig_info(enum type_of_stuff type_of_stuff, char *hostname, + rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf, + ushort_t port, struct t_info *tinfo, struct t_bind *tbind, + caddr_t *fhp, bool_t direct_to_server, char *fspath, + enum clnt_stat *cstat, mfs_snego_t *mfssnego) +{ + struct netconfig *nb = NULL; + int ping_server = 0; + + + if (nconf == NULL) + return (NULL); + + switch (type_of_stuff) { + case SERVER_FH: + nb = get_server_fh(hostname, prog, vers, mfssnego, + nconf, port, tinfo, tbind, fhp, direct_to_server, + fspath, cstat); + break; + case SERVER_PING: + ping_server = 1; + case SERVER_ADDR: + nb = get_server_addrorping(hostname, prog, vers, + nconf, port, tinfo, tbind, fhp, direct_to_server, + fspath, cstat, ping_server); + break; + default: + assert(nb != NULL); + } + return (nb); +} + +/* + * Get the server address or can we ping it or not. + * Check the portmap cache first for server address. + * If no entries there, ping the server with a NULLPROC rpc. + */ +void * +get_server_addrorping(char *hostname, rpcprog_t prog, rpcvers_t vers, + struct netconfig *nconf, ushort_t port, struct t_info *tinfo, + struct t_bind *tbind, caddr_t *fhp, bool_t direct_to_server, + char *fspath, enum clnt_stat *cstat, int ping_server) +{ + struct timeval tv; + enum clnt_stat cs = RPC_TIMEDOUT; + struct netbuf *nb = NULL; + CLIENT *cl = NULL; + int fd = -1; + + if (prog == NFS_PROGRAM && vers == NFS_V4) + if (strncasecmp(nconf->nc_proto, NC_UDP, strlen(NC_UDP)) == 0) + goto done; + + if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) < 0) { + goto done; + } + + /* LINTED pointer alignment */ + if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR)) + == NULL) { + goto done; + } + + if (direct_to_server != TRUE) { + if (!ping_server) { + if (get_cached_srv_addr(hostname, prog, vers, + nconf, &tbind->addr) == 0) + goto done; + } else { + if (port == 0) + goto done; + } + } + if (setup_nb_parms(nconf, tbind, tinfo, hostname, + fd, direct_to_server, port, prog, vers, 0) < 0) + goto done; + + if (port || (direct_to_server == TRUE)) { + tv.tv_sec = 10; + tv.tv_usec = 0; + cl = clnt_tli_create(fd, nconf, &tbind->addr, + prog, vers, 0, 0); + if (cl == NULL) + goto done; + + cs = clnt_call(cl, NULLPROC, xdr_void, 0, + xdr_void, 0, tv); + if (cs != RPC_SUCCESS) { + syslog(LOG_ERR, "error is %d", cs); + goto done; + } + } + if (!ping_server) { + nb = (struct netbuf *)malloc(sizeof (struct netbuf)); + if (nb == NULL) { + syslog(LOG_ERR, "no memory\n"); + goto done; + } + nb->buf = (char *)malloc(tbind->addr.maxlen); + if (nb->buf == NULL) { + syslog(LOG_ERR, "no memory\n"); + free(nb); + nb = NULL; + goto done; + } + (void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len); + nb->len = tbind->addr.len; + nb->maxlen = tbind->addr.maxlen; + cs = RPC_SUCCESS; + } +done: + destroy_auth_client_handle(cl); + cleanup_tli_parms(tbind, fd); + *cstat = cs; + return (nb); +} diff --git a/usr/src/cmd/fs.d/autofs/autod_xdr.c b/usr/src/cmd/fs.d/autofs/autod_xdr.c index 262a518eff..221ebcd2ce 100644 --- a/usr/src/cmd/fs.d/autofs/autod_xdr.c +++ b/usr/src/cmd/fs.d/autofs/autod_xdr.c @@ -21,12 +21,10 @@ /* * autod_xdr.c * - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * This file can not be automatically generated by rpcgen from * autofs_prot.x because of the xdr routines that provide readdir @@ -37,6 +35,8 @@ #include <sys/sysmacros.h> /* includes roundup() */ #include <string.h> #include <rpcsvc/autofs_prot.h> +#include <nfs/nfs4.h> +#include <rpcsvc/nfs4_prot.h> #include <rpc/xdr.h> #include <stdlib.h> #include <strings.h> diff --git a/usr/src/cmd/fs.d/nfs/Makefile b/usr/src/cmd/fs.d/nfs/Makefile index 9d737f3004..4687ae9d04 100644 --- a/usr/src/cmd/fs.d/nfs/Makefile +++ b/usr/src/cmd/fs.d/nfs/Makefile @@ -19,9 +19,8 @@ # CDDL HEADER END # # -#ident "%Z%%M% %I% %E% SMI" # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # cmd/fs.d/nfs/Makefile @@ -36,7 +35,8 @@ SUBDIR1= exportfs nfsd rquotad \ statd nfsstat mountd dfshares \ nfsfind nfs4cbd share SUBDIR2= clear_locks umount showmount \ - mount dfmounts nfslog nfsmapid + mount dfmounts nfslog nfsmapid \ + nfsref rp_basic SUBDIR3= etc svc SUBDIRS= $(SUBDIR1) $(SUBDIR2) $(SUBDIR3) diff --git a/usr/src/cmd/fs.d/nfs/lib/nfs_resolve.c b/usr/src/cmd/fs.d/nfs/lib/nfs_resolve.c new file mode 100644 index 0000000000..4951afd001 --- /dev/null +++ b/usr/src/cmd/fs.d/nfs/lib/nfs_resolve.c @@ -0,0 +1,386 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * Helper routines for nfsmapid and autod daemon + * to translate hostname to IP address and Netinfo. + */ +#include <stdio.h> +#include <stdlib.h> +#include <alloca.h> +#include <signal.h> +#include <libintl.h> +#include <limits.h> +#include <errno.h> +#include <sys/types.h> +#include <string.h> +#include <memory.h> +#include <pwd.h> +#include <grp.h> +#include <door.h> +#include <syslog.h> +#include <fcntl.h> +#include <unistd.h> +#include <assert.h> +#include <deflt.h> +#include <netdir.h> +#include <nfs/nfs4.h> +#include <nfs/nfssys.h> +#include <nfs/nfsid_map.h> +#include <nfs/mapid.h> +#include <nfs/nfs_sec.h> +#include <sys/sdt.h> +#include <sys/idmap.h> +#include <idmap.h> +#include <sys/fs/autofs.h> +#include "nfs_resolve.h" + +void +free_knconf(struct knetconfig *k) +{ + if (k == NULL) + return; + if (k->knc_protofmly) + free(k->knc_protofmly); + if (k->knc_proto) + free(k->knc_proto); + free(k); +} + +struct knetconfig * +get_knconf(struct netconfig *nconf) +{ + struct stat stbuf; + struct knetconfig *k = NULL; + int len; + + if (stat(nconf->nc_device, &stbuf) < 0) { + syslog(LOG_ERR, "get_knconf: stat %s: %m", nconf->nc_device); + return (NULL); + } + k = (struct knetconfig *)malloc(sizeof (*k)); + if (k == NULL) + goto nomem; + k->knc_semantics = nconf->nc_semantics; + + len = strlen(nconf->nc_protofmly); + if (len <= 0) + goto err_out; + k->knc_protofmly = malloc(KNC_STRSIZE); + if (k->knc_protofmly == NULL) + goto nomem; + bzero(k->knc_protofmly, KNC_STRSIZE); + bcopy(nconf->nc_protofmly, k->knc_protofmly, len); + + len = strlen(nconf->nc_proto); + if (len <= 0) + goto err_out; + k->knc_proto = malloc(KNC_STRSIZE); + if (k->knc_proto == NULL) + goto nomem; + bzero(k->knc_proto, KNC_STRSIZE); + bcopy(nconf->nc_proto, k->knc_proto, len); + + k->knc_rdev = stbuf.st_rdev; + + return (k); + +nomem: + syslog(LOG_ERR, "get_knconf: no memory"); +err_out: + if (k != NULL) + (void) free_knconf(k); + return (NULL); +} + +/* + * Get the information needed for an NFSv4.x referral. This + * information includes the netbuf, netname and knconfig. + */ +struct nfs_fsl_info * +get_nfs4ref_info(char *host, int port, int nfsver) +{ + char netname[MAXNETNAMELEN + 1]; + enum clnt_stat cstat; + struct nfs_fsl_info *fsl_retp = NULL; + struct netconfig *netconf = NULL; + char *nametemp, *namex = NULL; + struct netbuf *nb = NULL; + NCONF_HANDLE *nc = NULL; + + fsl_retp = calloc(1, sizeof (struct nfs_fsl_info)); + if (fsl_retp == NULL) { + syslog(LOG_ERR, "get_nfs4ref_info: no memory\n"); + return (NULL); + } + + nametemp = malloc(MAXNETNAMELEN + 1); + if (nametemp == NULL) { + free(fsl_retp); + return (NULL); + } + host2netname(nametemp, host, NULL); + namex = calloc(1, strlen(nametemp) + 1); + if (namex == NULL) { + free(nametemp); + free(fsl_retp); + return (NULL); + } + strncpy(namex, nametemp, strlen(nametemp)); + free(nametemp); + fsl_retp->netname = namex; + fsl_retp->netnm_len = strlen(namex) + 1; + + fsl_retp->addr = resolve_netconf(host, NFS_PROGRAM, nfsver, + &netconf, port, NULL, NULL, TRUE, NULL, &cstat); + + if (netconf == NULL || fsl_retp->addr == NULL) + goto done; + + fsl_retp->knconf = get_knconf(netconf); + if (fsl_retp->knconf == NULL) + goto done; + fsl_retp->knconf_len = (sizeof (struct knetconfig) + + (KNC_STRSIZE * 2)); + fsl_retp->netbuf_len = (sizeof (struct netbuf) + + fsl_retp->addr->maxlen); + return (fsl_retp); +done: + free_nfs4ref_info(fsl_retp); + return (NULL); +} + +void +free_nfs4ref_info(struct nfs_fsl_info *fsl_retp) +{ + if (fsl_retp == NULL) + return; + free_knconf(fsl_retp->knconf); + free(fsl_retp->netname); + if (fsl_retp->addr != NULL) { + free(fsl_retp->addr->buf); + free(fsl_retp->addr); + } + free(fsl_retp); +} + +void +cleanup_tli_parms(struct t_bind *tbind, int fd) +{ + if (tbind != NULL) { + t_free((char *)tbind, T_BIND); + tbind = NULL; + } + if (fd >= 0) + (void) t_close(fd); + fd = -1; +} + +struct netbuf * +resolve_netconf(char *host, rpcprog_t prog, rpcvers_t nfsver, + struct netconfig **netconf, ushort_t port, + struct t_info *tinfo, caddr_t *fhp, bool_t direct_to_server, + char *fspath, enum clnt_stat *cstatp) +{ + NCONF_HANDLE *nc; + struct netconfig *nconf = NULL; + int nthtry = FIRST_TRY; + struct netbuf *nb; + enum clnt_stat cstat; + + nc = setnetpath(); + if (nc == NULL) + goto done; +retry: + while (nconf = getnetpath(nc)) { + if (nconf->nc_flag & NC_VISIBLE) { + if (nthtry == FIRST_TRY) { + if ((nconf->nc_semantics == + NC_TPI_COTS_ORD) || + (nconf->nc_semantics == + NC_TPI_COTS)) { + if (port == 0) + break; + if ((strcmp(nconf->nc_protofmly, + NC_INET) == 0 || + strcmp(nconf->nc_protofmly, + NC_INET6) == 0) && + (strcmp(nconf->nc_proto, + NC_TCP) == 0)) + break; + } + } + if (nthtry == SECOND_TRY) { + if (nconf->nc_semantics == + NC_TPI_CLTS) { + if (port == 0) + break; + if ((strcmp(nconf->nc_protofmly, + NC_INET) == 0 || + strcmp(nconf->nc_protofmly, + NC_INET6) == 0) && + (strcmp(nconf->nc_proto, + NC_UDP) == 0)) + break; + } + } + } + } /* while */ + if (nconf == NULL) { + if (++nthtry <= MNT_PREF_LISTLEN) { + endnetpath(nc); + if ((nc = setnetpath()) == NULL) + goto done; + goto retry; + } else + return (NULL); + } else { + nb = get_server_addr(host, NFS_PROGRAM, nfsver, + nconf, port, NULL, NULL, TRUE, NULL, &cstat); + if (cstat != RPC_SUCCESS) + goto retry; + } +done: + *netconf = nconf; + *cstatp = cstat; + if (nc) + endnetpath(nc); + return (nb); +} + +int +setup_nb_parms(struct netconfig *nconf, struct t_bind *tbind, + struct t_info *tinfo, char *hostname, int fd, bool_t direct_to_server, + ushort_t port, rpcprog_t prog, rpcvers_t vers, bool_t file_handle) +{ + if (nconf == NULL) { + return (-1); + } + if (direct_to_server == TRUE) { + struct nd_hostserv hs; + struct nd_addrlist *retaddrs; + hs.h_host = hostname; + + if (port == 0) + hs.h_serv = "nfs"; + else + hs.h_serv = NULL; + + if (netdir_getbyname(nconf, &hs, &retaddrs) != ND_OK) { + return (-1); + } + memcpy(tbind->addr.buf, retaddrs->n_addrs->buf, + retaddrs->n_addrs->len); + tbind->addr.len = retaddrs->n_addrs->len; + tbind->addr.maxlen = retaddrs->n_addrs->maxlen; + netdir_free((void *)retaddrs, ND_ADDRLIST); + if (port) { + /* LINTED pointer alignment */ + if (strcmp(nconf->nc_protofmly, NC_INET) == NULL) + ((struct sockaddr_in *) + tbind->addr.buf)->sin_port = + htons((ushort_t)port); + else if (strcmp(nconf->nc_protofmly, NC_INET6) == NULL) + ((struct sockaddr_in6 *) + tbind->addr.buf)->sin6_port = + htons((ushort_t)port); + } + + if (file_handle) { + if (netdir_options(nconf, ND_SET_RESERVEDPORT, fd, + NULL) == -1) + return (-1); + } + } else if (!file_handle) { + if (port) { + /* LINTED pointer alignment */ + if (strcmp(nconf->nc_protofmly, NC_INET) == NULL) + ((struct sockaddr_in *) + tbind->addr.buf)->sin_port = + htons((ushort_t)port); + else if (strcmp(nconf->nc_protofmly, NC_INET6) == NULL) + ((struct sockaddr_in6 *) + tbind->addr.buf)->sin6_port = + htons((ushort_t)port); + } + } else { + return (-1); + } + return (1); +} + +/* + * Sets up TLI interface and finds the address withe netdir_getbyname(). + * returns the address returned from the call. + * Caller frees up the memory allocated here. + */ +struct netbuf * +get_server_addr(char *hostname, rpcprog_t prog, rpcvers_t vers, + struct netconfig *nconf, ushort_t port, + struct t_info *tinfo, caddr_t *fhp, bool_t direct_to_server, + char *fspath, enum clnt_stat *cstat) +{ + int fd = -1; + struct t_bind *tbind = NULL; + enum clnt_stat cs = RPC_SYSTEMERROR; + struct netbuf *nb = NULL; + int ret = -1; + + if (prog == NFS_PROGRAM && vers == NFS_V4) + if (strncasecmp(nconf->nc_proto, NC_UDP, strlen(NC_UDP)) == 0) + goto done; + + if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) < 0) + goto done; + + if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR)) == NULL) + goto done; + + if (setup_nb_parms(nconf, tbind, tinfo, hostname, fd, direct_to_server, + port, prog, vers, 0) < 0) + goto done; + + nb = (struct netbuf *)malloc(sizeof (struct netbuf)); + if (nb == NULL) { + syslog(LOG_ERR, "no memory\n"); + goto done; + } + nb->buf = (char *)malloc(tbind->addr.maxlen); + if (nb->buf == NULL) { + syslog(LOG_ERR, "no memory\n"); + free(nb); + nb = NULL; + goto done; + } + (void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len); + nb->len = tbind->addr.len; + nb->maxlen = tbind->addr.maxlen; + cs = RPC_SUCCESS; +done: + *cstat = cs; + cleanup_tli_parms(tbind, fd); + return (nb); +} diff --git a/usr/src/cmd/fs.d/nfs/lib/nfs_resolve.h b/usr/src/cmd/fs.d/nfs/lib/nfs_resolve.h new file mode 100644 index 0000000000..0b3055c123 --- /dev/null +++ b/usr/src/cmd/fs.d/nfs/lib/nfs_resolve.h @@ -0,0 +1,50 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _NFS_RESOLVE_H +#define _NFS_RESOLVE_H + +/* number of transports to try */ +#define MNT_PREF_LISTLEN 2 +#define FIRST_TRY 1 +#define SECOND_TRY 2 + +extern struct knetconfig *get_knconf(struct netconfig *); +extern void free_knconf(struct knetconfig *); +extern bool_t xdr_nfs_fsl_info(XDR *, struct nfs_fsl_info *); +extern struct netconfig *get_netconfig(NCONF_HANDLE *, ushort_t, char *); +extern int setup_nb_parms(struct netconfig *, struct t_bind *, struct t_info *, + char *, int, bool_t, ushort_t, rpcprog_t, rpcvers_t, int); +extern void cleanup_tli_parms(struct t_bind *, int); +extern struct nfs_fsl_info *get_nfs4ref_info(char *, int, int); +extern void free_nfs4ref_info(struct nfs_fsl_info *); +extern struct netbuf *get_server_addr(char *, rpcprog_t, rpcvers_t, + struct netconfig *, ushort_t, struct t_info *, caddr_t *, + bool_t, char *, enum clnt_stat *); +extern struct netbuf *resolve_netconf(char *, rpcprog_t, rpcvers_t, + struct netconfig **, ushort_t, struct t_info *, + caddr_t *, bool_t, char *, enum clnt_stat *); + +#endif /* _NFS_RESOLVE_H */ diff --git a/usr/src/cmd/fs.d/nfs/lib/ref_subr.c b/usr/src/cmd/fs.d/nfs/lib/ref_subr.c new file mode 100644 index 0000000000..b4980593d7 --- /dev/null +++ b/usr/src/cmd/fs.d/nfs/lib/ref_subr.c @@ -0,0 +1,311 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <stdio.h> +#include <unistd.h> +#include <strings.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/errno.h> +#include <limits.h> +#include <libnvpair.h> +#include <dlfcn.h> +#include <link.h> +#include <rp_plugin.h> +#include <fcntl.h> +#include <uuid/uuid.h> +#include <rpc/types.h> +#include <rpc/xdr.h> +#include <rpc/auth.h> +#include <rpc/clnt.h> +#include <rpc/rpc_msg.h> +#include <sys/param.h> +#include <nfs/nfs4.h> +#include <rpcsvc/nfs4_prot.h> + +/* + * str_to_utf8 - converts a null-terminated C string to a utf8 string + */ +utf8string * +str_to_utf8(char *nm, utf8string *str) +{ + int len; + + if (str == NULL) + return (NULL); + + if (nm == NULL || *nm == '\0') { + str->utf8string_len = 0; + str->utf8string_val = NULL; + return (NULL); + } + + len = strlen(nm); + + str->utf8string_val = malloc(len); + if (str->utf8string_val == NULL) { + str->utf8string_len = 0; + return (NULL); + } + str->utf8string_len = len; + bcopy(nm, str->utf8string_val, len); + + return (str); +} + +/* + * Converts a utf8 string to a C string. + * kmem_allocs a new string if not supplied + */ +char * +utf8_to_str(utf8string *str, uint_t *lenp, char *s) +{ + char *sp; + char *u8p; + int len; + int i; + + if (str == NULL) + return (NULL); + + u8p = str->utf8string_val; + len = str->utf8string_len; + if (len <= 0 || u8p == NULL) { + if (s) + *s = '\0'; + return (NULL); + } + + sp = s; + if (sp == NULL) + sp = malloc(len + 1); + if (sp == NULL) + return (NULL); + + /* + * At least check for embedded nulls + */ + for (i = 0; i < len; i++) { + sp[i] = u8p[i]; + if (u8p[i] == '\0') { + if (s == NULL) + free(sp); + return (NULL); + } + } + sp[len] = '\0'; + *lenp = len + 1; + + return (sp); +} + +void +print_referral_summary(fs_locations4 *fsl) +{ + int i, j; + uint_t l; + char *s; + fs_location4 *fs; + + if (fsl == NULL) { + printf("NULL\n"); + return; + } + + for (i = 0; i < fsl->locations.locations_len; i++) { + if (i > 0) + printf("\n"); + fs = &fsl->locations.locations_val[i]; + for (j = 0; j < fs->server.server_len; j++) { + s = utf8_to_str(&fs->server.server_val[j], &l, NULL); + if (j > 0) + printf(","); + printf("%s", s ? s : ""); + if (s) + free(s); + } + printf(":"); + for (j = 0; j < fs->rootpath.pathname4_len; j++) { + s = utf8_to_str(&fs->rootpath.pathname4_val[j], + &l, NULL); + printf("/%s", s ? s : ""); + if (s) + free(s); + } + if (fs->rootpath.pathname4_len == 0) + printf("/"); + } + printf("\n"); +} + +/* + * There is a kernel copy of this routine in nfs4_srv.c. + * Changes should be kept in sync. + */ +static int +nfs4_create_components(char *path, component4 *comp4) +{ + int slen, plen, ncomp; + char *ori_path, *nxtc, buf[MAXNAMELEN]; + + if (path == NULL) + return (0); + + plen = strlen(path) + 1; /* include the terminator */ + ori_path = path; + ncomp = 0; + + /* count number of components in the path */ + for (nxtc = path; nxtc < ori_path + plen; nxtc++) { + if (*nxtc == '/' || *nxtc == '\0' || *nxtc == '\n') { + if ((slen = nxtc - path) == 0) { + path = nxtc + 1; + continue; + } + + if (comp4 != NULL) { + bcopy(path, buf, slen); + buf[slen] = '\0'; + if (str_to_utf8(buf, &comp4[ncomp]) == NULL) + return (NULL); + } + + ncomp++; /* 1 valid component */ + path = nxtc + 1; + } + if (*nxtc == '\0' || *nxtc == '\n') + break; + } + + return (ncomp); +} + +/* + * There is a kernel copy of this routine in nfs4_srv.c. + * Changes should be kept in sync. + */ +int +make_pathname4(char *path, pathname4 *pathname) +{ + int ncomp; + component4 *comp4; + + if (pathname == NULL) + return (0); + + if (path == NULL) { + pathname->pathname4_val = NULL; + pathname->pathname4_len = 0; + return (0); + } + + /* count number of components to alloc buffer */ + if ((ncomp = nfs4_create_components(path, NULL)) == 0) { + pathname->pathname4_val = NULL; + pathname->pathname4_len = 0; + return (0); + } + comp4 = calloc(ncomp * sizeof (component4), 1); + if (comp4 == NULL) { + pathname->pathname4_val = NULL; + pathname->pathname4_len = 0; + return (0); + } + + /* copy components into allocated buffer */ + ncomp = nfs4_create_components(path, comp4); + + pathname->pathname4_val = comp4; + pathname->pathname4_len = ncomp; + + return (ncomp); +} + +bool_t +xdr_component4(register XDR *xdrs, component4 *objp) +{ + + if (!xdr_utf8string(xdrs, objp)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_utf8string(register XDR *xdrs, utf8string *objp) +{ + + if (xdrs->x_op != XDR_FREE) + return (xdr_bytes(xdrs, (char **)&objp->utf8string_val, + (uint_t *)&objp->utf8string_len, NFS4_MAX_UTF8STRING)); + return (TRUE); +} + +bool_t +xdr_pathname4(register XDR *xdrs, pathname4 *objp) +{ + + if (!xdr_array(xdrs, (char **)&objp->pathname4_val, + (uint_t *)&objp->pathname4_len, NFS4_MAX_PATHNAME4, + sizeof (component4), (xdrproc_t)xdr_component4)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fs_location4(register XDR *xdrs, fs_location4 *objp) +{ + + if (xdrs->x_op == XDR_DECODE) { + objp->server.server_val = NULL; + objp->rootpath.pathname4_val = NULL; + } + if (!xdr_array(xdrs, (char **)&objp->server.server_val, + (uint_t *)&objp->server.server_len, ~0, + sizeof (utf8string), (xdrproc_t)xdr_utf8string)) + return (FALSE); + if (!xdr_pathname4(xdrs, &objp->rootpath)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_fs_locations4(register XDR *xdrs, fs_locations4 *objp) +{ + + if (xdrs->x_op == XDR_DECODE) { + objp->fs_root.pathname4_len = 0; + objp->fs_root.pathname4_val = NULL; + objp->locations.locations_val = NULL; + } + if (!xdr_pathname4(xdrs, &objp->fs_root)) + return (FALSE); + if (!xdr_array(xdrs, (char **)&objp->locations.locations_val, + (uint_t *)&objp->locations.locations_len, ~0, + sizeof (fs_location4), (xdrproc_t)xdr_fs_location4)) + return (FALSE); + return (TRUE); +} diff --git a/usr/src/cmd/fs.d/nfs/lib/ref_subr.h b/usr/src/cmd/fs.d/nfs/lib/ref_subr.h new file mode 100644 index 0000000000..6550411a10 --- /dev/null +++ b/usr/src/cmd/fs.d/nfs/lib/ref_subr.h @@ -0,0 +1,57 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _REF_SUBR_H +#define _REF_SUBR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> +#include <rpc/types.h> +#include <rpc/xdr.h> +#include <rpc/auth.h> +#include <rpc/clnt.h> +#include <rpc/rpc_msg.h> +#include <nfs/nfs4.h> +#include <rpcsvc/nfs4_prot.h> + +extern utf8string *str_to_utf8(char *, utf8string *); +extern char *utf8_to_str(utf8string *, uint_t *, char *); +extern void print_referral_summary(fs_locations4 *); +extern int make_pathname4(char *, pathname4 *); +extern bool_t xdr_component4(register XDR *, component4 *); +extern bool_t xdr_utf8string(register XDR *, utf8string *); +extern bool_t xdr_pathname4(register XDR *, pathname4 *); +extern bool_t xdr_fs_location4(register XDR *, fs_location4 *); +extern bool_t xdr_fs_locations4(register XDR *, fs_locations4 *); + +#ifdef __cplusplus +} +#endif + +#endif /* _REF_SUBR_H */ diff --git a/usr/src/cmd/fs.d/nfs/nfsmapid/Makefile b/usr/src/cmd/fs.d/nfs/nfsmapid/Makefile index 3a7cf93211..7d29271eb0 100644 --- a/usr/src/cmd/fs.d/nfs/nfsmapid/Makefile +++ b/usr/src/cmd/fs.d/nfs/nfsmapid/Makefile @@ -32,12 +32,14 @@ include ../../Makefile.fstype LDLIBS += -L$(ROOT)/usr/lib/nfs -R/usr/lib/nfs LDLIBS += -lnsl -lmapid -ldtrace -lidmap -SRCS = nfsmapid.c nfsmapid_server.c +COMMON = nfs_resolve.o +SRCS = nfsmapid.c ../lib/nfs_resolve.c nfsmapid_server.c DSRC = nfsmapid_dt.d DOBJ = $(DSRC:%.d=%.o) -OBJS = $(SRCS:%.c=%.o) +OBJS = nfsmapid.o nfsmapid_server.o $(COMMON) CPPFLAGS += -I../lib -D_POSIX_PTHREAD_SEMANTICS + all: $(TYPEPROG) $(TESTPROG) $(TYPEPROG): $(OBJS) $(DSRC) @@ -45,6 +47,9 @@ $(TYPEPROG): $(OBJS) $(DSRC) $(LINK.c) $(ZIGNORE) -o $@ $(DOBJ) $(OBJS) $(LDLIBS) $(POST_PROCESS) +nfs_resolve.o: ../lib/nfs_resolve.c + $(COMPILE.c) ../lib/nfs_resolve.c + TESTSRCS = nfsmapid_test.c TESTOBJS = $(TESTSRCS:%.c=%.o) TEST_OBJS = $(TESTOBJS) diff --git a/usr/src/cmd/fs.d/nfs/nfsmapid/nfsmapid_server.c b/usr/src/cmd/fs.d/nfs/nfsmapid/nfsmapid_server.c index e8ec47503a..a80263cd51 100644 --- a/usr/src/cmd/fs.d/nfs/nfsmapid/nfsmapid_server.c +++ b/usr/src/cmd/fs.d/nfs/nfsmapid/nfsmapid_server.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -53,6 +53,9 @@ #include <sys/sdt.h> #include <sys/idmap.h> #include <idmap.h> +#include <sys/fs/autofs.h> +#include <sys/mkdev.h> +#include "nfs_resolve.h" #define UID_MAX_STR_LEN 11 /* Digits in UID_MAX + 1 */ #define DIAG_FILE "/var/run/nfs4_domain" @@ -585,6 +588,97 @@ done: } } +void +nfsmapid_server_netinfo(refd_door_args_t *referral_args, size_t arg_size) +{ + char *res; + int res_size; + int error; + int srsz = 0; + char host[MAXHOSTNAMELEN]; + utf8string *nfsfsloc_args; + refd_door_res_t *door_res; + refd_door_res_t failed_res; + struct nfs_fsl_info *nfs_fsloc_res; + + if (arg_size < sizeof (refd_door_args_t)) { + failed_res.res_status = EINVAL; + res = (char *)&failed_res; + res_size = sizeof (refd_door_res_t); + syslog(LOG_ERR, + "nfsmapid_server_netinfo failed: Invalid data\n"); + goto send_response; + } + + if (decode_args(xdr_utf8string, (refd_door_args_t *)referral_args, + (caddr_t *)&nfsfsloc_args, sizeof (utf8string))) { + syslog(LOG_ERR, "cannot allocate memory"); + failed_res.res_status = ENOMEM; + failed_res.xdr_len = 0; + res = (caddr_t)&failed_res; + res_size = sizeof (refd_door_res_t); + goto send_response; + } + + if (nfsfsloc_args->utf8string_len >= MAXHOSTNAMELEN) { + syslog(LOG_ERR, "argument too large"); + failed_res.res_status = EOVERFLOW; + failed_res.xdr_len = 0; + res = (caddr_t)&failed_res; + res_size = sizeof (refd_door_res_t); + goto send_response; + } + + snprintf(host, nfsfsloc_args->utf8string_len + 1, + "%s", nfsfsloc_args->utf8string_val); + + nfs_fsloc_res = + get_nfs4ref_info(host, NFS_PORT, NFS_V4); + + xdr_free(xdr_utf8string, (char *)&nfsfsloc_args); + + if (nfs_fsloc_res) { + error = 0; + error = encode_res(xdr_nfs_fsl_info, &door_res, + (caddr_t)nfs_fsloc_res, &res_size); + free_nfs4ref_info(nfs_fsloc_res); + if (error != 0) { + syslog(LOG_ERR, + "error allocating fs_locations " + "results buffer"); + failed_res.res_status = error; + failed_res.xdr_len = srsz; + res = (caddr_t)&failed_res; + res_size = sizeof (refd_door_res_t); + } else { + door_res->res_status = 0; + res = (caddr_t)door_res; + } + } else { + failed_res.res_status = EINVAL; + failed_res.xdr_len = 0; + res = (caddr_t)&failed_res; + res_size = sizeof (refd_door_res_t); + } + +send_response: + srsz = res_size; + errno = 0; + + error = door_return(res, res_size, NULL, 0); + if (errno == E2BIG) { + failed_res.res_status = EOVERFLOW; + failed_res.xdr_len = srsz; + res = (caddr_t)&failed_res; + res_size = sizeof (refd_door_res_t); + } else { + res = NULL; + res_size = 0; + } + + door_return(res, res_size, NULL, 0); +} + /* ARGSUSED */ void nfsmapid_func(void *cookie, char *argp, size_t arg_size, @@ -592,6 +686,7 @@ nfsmapid_func(void *cookie, char *argp, size_t arg_size, { struct mapid_arg *mapargp; struct mapid_res mapres; + refd_door_args_t *referral_args; /* * Make sure we have a valid argument @@ -606,6 +701,7 @@ nfsmapid_func(void *cookie, char *argp, size_t arg_size, /* LINTED pointer cast */ mapargp = (struct mapid_arg *)argp; + referral_args = (refd_door_args_t *)argp; switch (mapargp->cmd) { case NFSMAPID_STR_UID: nfsmapid_str_uid(mapargp, arg_size); @@ -619,6 +715,8 @@ nfsmapid_func(void *cookie, char *argp, size_t arg_size, case NFSMAPID_GID_STR: nfsmapid_gid_str(mapargp, arg_size); return; + case NFSMAPID_SRV_NETINFO: + nfsmapid_server_netinfo(referral_args, arg_size); default: break; } @@ -796,3 +894,190 @@ cb_update_domain(void *arg) return (NULL); } + +bool_t +xdr_utf8string(XDR *xdrs, utf8string *objp) +{ + if (xdrs->x_op != XDR_FREE) + return (xdr_bytes(xdrs, (char **)&objp->utf8string_val, + (uint_t *)&objp->utf8string_len, NFS4_MAX_UTF8STRING)); + return (TRUE); +} + + +int +decode_args(xdrproc_t xdrfunc, refd_door_args_t *argp, caddr_t *xdrargs, + int size) +{ + XDR xdrs; + + caddr_t tmpargs = (caddr_t)&((refd_door_args_t *)argp)->xdr_arg; + size_t arg_size = ((refd_door_args_t *)argp)->xdr_len; + + xdrmem_create(&xdrs, tmpargs, arg_size, XDR_DECODE); + + *xdrargs = calloc(1, size); + if (*xdrargs == NULL) { + syslog(LOG_ERR, "error allocating arguments buffer"); + return (ENOMEM); + } + + if (!(*xdrfunc)(&xdrs, *xdrargs)) { + free(*xdrargs); + *xdrargs = NULL; + syslog(LOG_ERR, "error decoding arguments"); + return (EINVAL); + } + + return (0); +} + +int +encode_res( + xdrproc_t xdrfunc, + refd_door_res_t **results, + caddr_t resp, + int *size) +{ + XDR xdrs; + + *size = xdr_sizeof((*xdrfunc), resp); + *results = malloc(sizeof (refd_door_res_t) + *size); + if (*results == NULL) { + return (ENOMEM); + } + (*results)->xdr_len = *size; + *size = sizeof (refd_door_res_t) + (*results)->xdr_len; + xdrmem_create(&xdrs, (caddr_t)((*results)->xdr_res), + (*results)->xdr_len, XDR_ENCODE); + if (!(*xdrfunc)(&xdrs, resp)) { + (*results)->res_status = EINVAL; + syslog(LOG_ERR, "error encoding results"); + return ((*results)->res_status); + } + (*results)->res_status = 0; + return ((*results)->res_status); +} + + +bool_t +xdr_knetconfig(XDR *xdrs, struct knetconfig *objp) +{ + rpc_inline_t *buf; + int i; + u_longlong_t dev64; +#if !defined(_LP64) + uint32_t major, minor; +#endif + + if (!xdr_u_int(xdrs, &objp->knc_semantics)) + return (FALSE); + if (!xdr_opaque(xdrs, objp->knc_protofmly, KNC_STRSIZE)) + return (FALSE); + if (!xdr_opaque(xdrs, objp->knc_proto, KNC_STRSIZE)) + return (FALSE); + + /* + * For interoperability between 32-bit daemon and 64-bit kernel, + * we always treat dev_t as 64-bit number and do the expanding + * or compression of dev_t as needed. + * We have to hand craft the conversion since there is no available + * function in ddi.c. Besides ddi.c is available only in the kernel + * and we want to keep both user and kernel of xdr_knetconfig() the + * same for consistency. + */ + + if (xdrs->x_op == XDR_ENCODE) { +#if defined(_LP64) + dev64 = objp->knc_rdev; +#else + major = (objp->knc_rdev >> NBITSMINOR32) & MAXMAJ32; + minor = objp->knc_rdev & MAXMIN32; + dev64 = (((unsigned long long)major) << NBITSMINOR64) | minor; +#endif + if (!xdr_u_longlong_t(xdrs, &dev64)) + return (FALSE); + } + if (xdrs->x_op == XDR_DECODE) { +#if defined(_LP64) + if (!xdr_u_longlong_t(xdrs, (u_longlong_t *)&objp->knc_rdev)) + return (FALSE); +#else + if (!xdr_u_longlong_t(xdrs, &dev64)) + return (FALSE); + + major = (dev64 >> NBITSMINOR64) & L_MAXMAJ32; + minor = dev64 & L_MAXMIN32; + objp->knc_rdev = (major << L_BITSMINOR32) | minor; +#endif + } + + if (xdrs->x_op == XDR_ENCODE) { + buf = XDR_INLINE(xdrs, (8) * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8, + sizeof (uint_t), (xdrproc_t)xdr_u_int)) + return (FALSE); + } else { + uint_t *genp; + + for (i = 0, genp = objp->knc_unused; + i < 8; i++) { +#if defined(_LP64) || defined(_KERNEL) + IXDR_PUT_U_INT32(buf, *genp++); +#else + IXDR_PUT_U_LONG(buf, *genp++); +#endif + } + } + return (TRUE); + } else if (xdrs->x_op == XDR_DECODE) { + buf = XDR_INLINE(xdrs, (8) * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8, + sizeof (uint_t), (xdrproc_t)xdr_u_int)) + return (FALSE); + } else { + uint_t *genp; + + for (i = 0, genp = objp->knc_unused; + i < 8; i++) { +#if defined(_LP64) || defined(_KERNEL) + *genp++ = IXDR_GET_U_INT32(buf); +#else + *genp++ = IXDR_GET_U_LONG(buf); +#endif + } + } + return (TRUE); + } + + if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8, + sizeof (uint_t), (xdrproc_t)xdr_u_int)) + return (FALSE); + return (TRUE); +} + +/* + * used by NFSv4 referrals to get info needed for NFSv4 referral mount. + */ +bool_t +xdr_nfs_fsl_info(XDR *xdrs, struct nfs_fsl_info *objp) +{ + + if (!xdr_u_int(xdrs, &objp->netbuf_len)) + return (FALSE); + if (!xdr_u_int(xdrs, &objp->netnm_len)) + return (FALSE); + if (!xdr_u_int(xdrs, &objp->knconf_len)) + return (FALSE); + if (!xdr_string(xdrs, &objp->netname, ~0)) + return (FALSE); + if (!xdr_pointer(xdrs, (char **)&objp->addr, objp->netbuf_len, + (xdrproc_t)xdr_netbuf)) + return (FALSE); + if (!xdr_pointer(xdrs, (char **)&objp->knconf, + objp->knconf_len, (xdrproc_t)xdr_knetconfig)) + return (FALSE); + return (TRUE); +} diff --git a/usr/src/cmd/fs.d/nfs/nfsref/Makefile b/usr/src/cmd/fs.d/nfs/nfsref/Makefile new file mode 100644 index 0000000000..fa5d58460d --- /dev/null +++ b/usr/src/cmd/fs.d/nfs/nfsref/Makefile @@ -0,0 +1,69 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# cmd/fs.d/nfs/nfsref/Makefile + +PROG= nfsref + +POFILE= nfsref.po + +include ../../../Makefile.cmd + +CFLAGS += $(CCVERBOSE) -I../lib +OBJS= nfsref.o ref_subr.o +SRCS= nfsref.c + +LDLIBS += -lreparse -lnvpair -lnsl -lumem + +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(LDLIBS) $(OBJS) + $(POST_PROCESS) + +FILEMODE= 0555 + +.KEEP_STATE: + +all: $(PROG) + +ref_subr.o: ../lib/ref_subr.c + $(COMPILE.c) ../lib/ref_subr.c + +install: all $(ROOTUSRSBINPROG) + +catalog: $(POFILE) + +$(POFILE): $(SRCS) + $(RM) $@ + $(COMPILE.cpp) $(SRCS) > $(POFILE).i + $(XGETTEXT) $(XGETFLAGS) $(POFILE).i + sed "/^domain/d" messages.po > $@ + $(RM) $(POFILE).i messages.po + +lint: + $(LINT.c) $(SRCS) $(LDLIBS) + +clean: + $(RM) $(OBJS) + +include ../../../Makefile.targ diff --git a/usr/src/cmd/fs.d/nfs/nfsref/nfsref.c b/usr/src/cmd/fs.d/nfs/nfsref/nfsref.c new file mode 100644 index 0000000000..2fe520df0f --- /dev/null +++ b/usr/src/cmd/fs.d/nfs/nfsref/nfsref.c @@ -0,0 +1,401 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <stdio.h> +#include <unistd.h> +#include <strings.h> +#include <string.h> +#include <limits.h> +#include <libnvpair.h> +#include <locale.h> +#include <sys/stat.h> +#include <sys/fs_reparse.h> +#include <rp_plugin.h> +#include <uuid/uuid.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <priv.h> +#include <nfs/nfs4.h> +#include <rpcsvc/nfs4_prot.h> +#include "ref_subr.h" + +#ifndef TEXT_DOMAIN +#define TEXT_DOMAIN "SUNW_OST_OSCMD" +#endif /* TEXT_DOMAIN */ + +extern int errno; + +void +usage() +{ + fprintf(stderr, gettext("Usage:\n")); + fprintf(stderr, + gettext("\tnfsref [-t type] add path location [location ...]\n")); + fprintf(stderr, gettext("\tnfsref [-t type] remove path\n")); + fprintf(stderr, gettext("\tnfsref [-t type] lookup path\n")); +} + +/* + * Copy a string from source to destination, escaping space + * with a backslash and escaping the escape character as well. + */ +int +add_escape(char *src, char *dest, int limit) +{ + char *sp, *dp; + int destlen = 0; + + sp = src; + dp = dest; + + while (*sp && destlen < limit) { + if (*sp == '\\') { + *dp++ = '\\'; + *dp++ = '\\'; + destlen++; + } else if (*sp == ' ') { + *dp++ = '\\'; + *dp++ = ' '; + destlen++; + } else + *dp++ = *sp; + destlen++; + sp++; + } + if (limit <= 0) + return (-1); + return (destlen); +} + +int +addref(char *sl_path, char *svc_type, int optind, int argc, char *argv[]) +{ + int err, fd, i, len, oldlen, notfound = 0; + char *text, *location; + nvlist_t *nvl = NULL; + char buf[SYMLINK_MAX]; + struct stat sbuf; + + /* Get an nvlist */ + nvl = reparse_init(); + if (nvl == NULL) + return (ENOMEM); + + /* Get the reparse point data, if the RP exists */ + err = readlink(sl_path, buf, SYMLINK_MAX); + if (err == -1) { + if (errno == ENOENT) { + notfound = 1; + err = 0; + } else { + reparse_free(nvl); + return (errno); + } + } else { + buf[err] = '\0'; + } + + /* Get any data into nvlist */ + if (notfound == 0) + err = reparse_parse(buf, nvl); + if (err != 0) { + reparse_free(nvl); + return (err); + } + + /* + * Accumulate multiple locations on the command line into 'buf' + */ + oldlen = len = 0; + location = NULL; + for (i = optind; i < argc; i++) { + bzero(buf, sizeof (buf)); + len += add_escape(argv[i], buf, SYMLINK_MAX) + 2; + location = realloc(location, len); + location[oldlen] = '\0'; + oldlen = len; + strlcat(location, buf, len); + strlcat(location, " ", len); + } + location[len - 2] = '\0'; + + /* Add to the list */ + err = reparse_add(nvl, svc_type, location); + if (err) { + reparse_free(nvl); + return (err); + } + + /* Get the new or modified symlink contents */ + err = reparse_unparse(nvl, &text); + reparse_free(nvl); + if (err) + return (err); + + /* Delete first if found */ + if (notfound == 0) { + err = reparse_delete(sl_path); + if (err) { + free(text); + return (err); + } + } + + /* Finally, write out the reparse point */ + err = reparse_create(sl_path, text); + free(text); + if (err) + return (err); + + err = lstat(sl_path, &sbuf); + if (err == 0 && strcasecmp(sbuf.st_fstype, "ZFS") != 0) + printf(gettext( + "Warning: referrals do not work on this filesystem\n")); + + if (notfound) + printf(gettext("Created reparse point %s\n"), sl_path); + else + printf(gettext("Added to reparse point %s\n"), sl_path); + + return (0); +} + +int +delref(char *sl_path, char *svc_type) +{ + char *cp; + char *svc_data; + int err; + nvlist_t *nvl; + nvpair_t *curr; + char buf[SYMLINK_MAX]; + int fd, fd2; + FILE *fp, *fp2; + char uuid[UUID_PRINTABLE_STRING_LENGTH], path[256], loc[2048]; + + /* Get an nvlist */ + if (!(nvl = reparse_init())) + return (ENOMEM); + + /* Get the symlink data (should be there) */ + err = readlink(sl_path, buf, SYMLINK_MAX); + if (err == -1) { + reparse_free(nvl); + return (errno); + } + buf[err] = '\0'; + + /* Get the records into the nvlist */ + err = reparse_parse(buf, nvl); + if (err) { + reparse_free(nvl); + return (err); + } + + /* Remove from nvlist */ + err = reparse_remove(nvl, svc_type); + if (err) { + reparse_free(nvl); + return (err); + } + + /* Any list entries left? If so, turn nvlist back to string. */ + curr = nvlist_next_nvpair(nvl, NULL); + if (curr != NULL) { + err = reparse_unparse(nvl, &cp); + reparse_free(nvl); + if (err) + return (err); + } else { + reparse_free(nvl); + cp = NULL; + } + + /* Finally, delete and perhaps recreate the reparse point */ + err = reparse_delete(sl_path); + if (err) { + free(cp); + return (err); + } + + if (cp != NULL) { + err = reparse_create(sl_path, cp); + free(cp); + if (err) + return (err); + } + printf(gettext("Removed svc_type '%s' from %s\n"), svc_type, sl_path); + return (err); +} + +int +lookup(char *sl_path, char *svc_type, int type_set) +{ + int err; + size_t bufsize; + char buf[1024]; + char *type, *svc_data; + nvlist_t *nvl; + nvpair_t *curr; + fs_locations4 fsl; + XDR xdr; + + if (!(nvl = reparse_init())) + return (-1); + + /* Get reparse point data */ + err = readlink(sl_path, buf, SYMLINK_MAX); + if (err == -1) + return (errno); + buf[err] = '\0'; + + /* Parse it to an nvlist */ + err = reparse_parse(buf, nvl); + if (err) { + reparse_free(nvl); + return (err); + } + + /* Look for entries of the requested service type */ + curr = NULL; + while ((curr = nvlist_next_nvpair(nvl, curr)) != NULL) { + type = nvpair_name(curr); + if (type_set && strcasecmp(type, svc_type) == 0) + break; + if (!type_set && strncasecmp(type, "nfs", 3) == 0) + break; + } + if (curr == NULL) { + reparse_free(nvl); + return (ENOENT); + } + + /* Get the service data and look it up */ + nvpair_value_string(curr, &svc_data); + + bufsize = sizeof (buf); + err = reparse_deref(type, svc_data, buf, &bufsize); + reparse_free(nvl); + if (err) + return (err); + + xdrmem_create(&xdr, buf, bufsize, XDR_DECODE); + err = xdr_fs_locations4(&xdr, &fsl); + XDR_DESTROY(&xdr); + if (err != TRUE) + return (ENOENT); + printf(gettext("%s points to: "), sl_path); + print_referral_summary(&fsl); + return (0); +} + +extern char *optarg; +extern int optind, optopt; + +int +main(int argc, char *argv[]) +{ + char c, *command, *sl_path, *svc_type; + int type_set, err; + + (void) setlocale(LC_ALL, ""); + (void) textdomain(TEXT_DOMAIN); + + svc_type = "nfs-basic"; /* Default from SMF some day */ + type_set = 0; /* Lookup any nfs type */ + + /* Look for options (just the service type now) */ + while ((c = getopt(argc, argv, "t:")) != -1) { + switch (c) { + case 't': + svc_type = optarg; + type_set = 1; + break; + + default: + usage(); + exit(1); + } + } + + /* Make sure there's at least a command and one argument */ + if (optind + 1 >= argc) { + usage(); + exit(1); + } + + err = rp_plugin_init(); + switch (err) { + case RP_OK: + break; + case RP_NO_PLUGIN_DIR: + fprintf(stderr, + gettext("Warning: no plugin directory, continuing...\n")); + break; + case RP_NO_PLUGIN: + fprintf(stderr, + gettext("Warning: no plugin found, continuing...\n")); + break; + case RP_NO_MEMORY: + fprintf(stderr, + gettext("rp_plugin_init failed, no memory\n")); + exit(0); + default: + fprintf(stderr, + gettext("rp_plugin_init failed, error %d\n"), err); + exit(0); + } + + command = argv[optind++]; + sl_path = argv[optind++]; + + if (strcmp(command, "add") == 0) { + + if (optind >= argc) { + usage(); + exit(1); + } + + err = addref(sl_path, svc_type, optind, argc, argv); + + } else if (strcmp(command, "remove") == 0) { + + err = delref(sl_path, svc_type); + + } else if (strcmp(command, "lookup") == 0) { + + err = lookup(sl_path, svc_type, type_set); + + } else { + usage(); + exit(1); + } + if (err != 0) + fprintf(stderr, gettext("Command %s failed: %s\n"), command, + strerror(err)); + return (err); +} diff --git a/usr/src/cmd/fs.d/nfs/nfsstat/nfsstat.c b/usr/src/cmd/fs.d/nfs/nfsstat/nfsstat.c index 5c406e331d..50b597d0f8 100644 --- a/usr/src/cmd/fs.d/nfs/nfsstat/nfsstat.c +++ b/usr/src/cmd/fs.d/nfs/nfsstat/nfsstat.c @@ -117,9 +117,10 @@ static int count; /* number of iterations the stat is printed */ /* * MI4_MIRRORMOUNT is canonically defined in nfs4_clnt.h, but we cannot - * include that file here. + * include that file here. Same with MI4_REFERRAL. */ #define MI4_MIRRORMOUNT 0x4000 +#define MI4_REFERRAL 0x8000 #define NFS_V4 4 static int req_width(kstat_t *, int); @@ -1241,6 +1242,8 @@ mi_print(void) if (mik.mik_vers >= NFS_V4) { if (mik.mik_flags & MI4_MIRRORMOUNT) printf(",mirrormount"); + if (mik.mik_flags & MI4_REFERRAL) + printf(",referral"); } printf(",rsize=%d,wsize=%d,retrans=%d,timeo=%d", diff --git a/usr/src/cmd/fs.d/nfs/rp_basic/Makefile b/usr/src/cmd/fs.d/nfs/rp_basic/Makefile new file mode 100644 index 0000000000..2c2af534c6 --- /dev/null +++ b/usr/src/cmd/fs.d/nfs/rp_basic/Makefile @@ -0,0 +1,56 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include $(SRC)/lib/Makefile.lib + +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +MSGFILES = libnfs_basic.c +POFILE = rp_basic.po + +all := TARGET= all +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install +lint := TARGET= lint + +.KEEP_STATE: + +all clean clobber install lint: $(SUBDIRS) + +catalog: $(POFILE) + +$(POFILE): $(MSGFILES) + $(BUILDPO.msgfiles) + +_msg: $(MSGDOMAINPOFILE) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include $(SRC)/lib/Makefile.targ +include $(SRC)/Makefile.msg.targ diff --git a/usr/src/cmd/fs.d/nfs/rp_basic/Makefile.com b/usr/src/cmd/fs.d/nfs/rp_basic/Makefile.com new file mode 100644 index 0000000000..85567a3537 --- /dev/null +++ b/usr/src/cmd/fs.d/nfs/rp_basic/Makefile.com @@ -0,0 +1,65 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +LIBRARY = libnfs_basic.a +VERS = .1 + +LIBOBJS = libnfs_basic.o +COMMON = ref_subr.o +OBJECTS = $(LIBOBJS) $(COMMON) + +include $(SRC)/lib/Makefile.lib + +lintcheck := SRCS = ../libnfs_basic.c ../../lib/ref_subr.c + +ROOTLIBDIR = $(ROOT)/usr/lib/reparse +ROOTLIBDIR64 = $(ROOT)/usr/lib/reparse/$(MACH64) + +LIBSRCS = $(LIBOBJS:%.o=$(SRCDIR)/%.c) + +LIBS = $(DYNLIB) +LDLIBS += -lc -lnsl + +CFLAGS += $(CCVERBOSE) +CPPFLAGS += -D_REENTRANT -I$(SRC)/cmd/fs.d/nfs/lib + +.KEEP_STATE: + +all: $(LIBS) + +install: $(ROOTLIBDIR) $(ROOTLIBDIR64) all + +lint: lintcheck + +pics/ref_subr.o: ../../lib/ref_subr.c + $(COMPILE.c) -o pics/ref_subr.o ../../lib/ref_subr.c + $(POST_PROCESS_O) + +$(ROOTLIBDIR): + $(INS.dir) + +$(ROOTLIBDIR64): + $(INS.dir) + +include $(SRC)/lib/Makefile.targ diff --git a/usr/src/cmd/fs.d/nfs/rp_basic/amd64/Makefile b/usr/src/cmd/fs.d/nfs/rp_basic/amd64/Makefile new file mode 100644 index 0000000000..f013cacc18 --- /dev/null +++ b/usr/src/cmd/fs.d/nfs/rp_basic/amd64/Makefile @@ -0,0 +1,28 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com +include $(SRC)/lib/Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/cmd/fs.d/nfs/rp_basic/i386/Makefile b/usr/src/cmd/fs.d/nfs/rp_basic/i386/Makefile new file mode 100644 index 0000000000..8673a37a6e --- /dev/null +++ b/usr/src/cmd/fs.d/nfs/rp_basic/i386/Makefile @@ -0,0 +1,27 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) diff --git a/usr/src/cmd/fs.d/nfs/rp_basic/libnfs_basic.c b/usr/src/cmd/fs.d/nfs/rp_basic/libnfs_basic.c new file mode 100644 index 0000000000..d49b2cc594 --- /dev/null +++ b/usr/src/cmd/fs.d/nfs/rp_basic/libnfs_basic.c @@ -0,0 +1,310 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <stdio.h> +#include <unistd.h> +#include <strings.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/errno.h> +#include <limits.h> +#include <libnvpair.h> +#include <dlfcn.h> +#include <link.h> +#include <rp_plugin.h> +#include <fcntl.h> +#include <uuid/uuid.h> +#include <rpc/types.h> +#include <rpc/xdr.h> +#include <rpc/auth.h> +#include <rpc/clnt.h> +#include <rpc/rpc_msg.h> +#include <sys/param.h> +#include <nfs/nfs4.h> +#include <rpcsvc/nfs4_prot.h> +#include "ref_subr.h" + +extern int errno; + +#define SERVICE_TYPE "nfs-basic" + +char *nfs_basic_service_type(void); +boolean_t nfs_basic_supports_svc(const char *); +int nfs_basic_deref(const char *, const char *, char *, size_t *); +int nfs_basic_form(const char *, const char *, char *, size_t *); + +struct rp_plugin_ops rp_plugin_ops = { + RP_PLUGIN_V1, + NULL, /* rpo_init */ + NULL, /* rpo_fini */ + nfs_basic_service_type, + nfs_basic_supports_svc, + nfs_basic_form, + nfs_basic_deref +}; + +/* + * What service type does this module support? + */ +char * +nfs_basic_service_type() +{ + return (SERVICE_TYPE); +} + +/* + * Does this module support a particular service type? + */ +boolean_t +nfs_basic_supports_svc(const char *svc_type) +{ + if (!svc_type) + return (0); + return (!strncasecmp(svc_type, SERVICE_TYPE, strlen(SERVICE_TYPE))); +} + +/* + * Take a string with a set of locations like this: + * host1:/path1 host2:/path2 host3:/path3 + * and convert it to an fs_locations4 for the deref routine. + */ +static fs_locations4 * +get_fs_locations(char *buf) +{ + fs_locations4 *result = NULL; + fs_location4 *fsl_array; + int i = 0, fsl_count = 0, gothost = 0, escape = 0, delimiter = 0; + int len; + char *p, *sp, *dp, buf2[SYMLINK_MAX]; + + if (buf == NULL) + return (NULL); +#ifdef DEBUG + printf("get_fs_locations: input %s\n", buf); +#endif + /* + * Count fs_location entries by counting spaces. + * Remember that escaped spaces ("\ ") may exist. + * We mark the location boundaries with null bytes. + * Variable use: + * escape - set if we have found a backspace, + * part of either "\ " or "\\" + * delimiter - set if we have found a space and + * used to skip multiple spaces + */ + for (sp = buf; sp && *sp; sp++) { + if (*sp == '\\') { + escape = 1; + delimiter = 0; + continue; + } + if (*sp == ' ') { + if (delimiter == 1) + continue; + if (escape == 0) { + delimiter = 1; + fsl_count++; + *sp = '\0'; + } else + escape = 0; + } else + delimiter = 0; + } + len = sp - buf; + sp--; + if (escape == 0 && *sp != '\0') + fsl_count++; +#ifdef DEBUG + printf("get_fs_locations: fsl_count %d\n", fsl_count); +#endif + if (fsl_count == 0) + goto out; + + /* Alloc space for everything */ + result = malloc(sizeof (fs_locations4)); + if (result == NULL) + goto out; + fsl_array = malloc(fsl_count * sizeof (fs_location4)); + if (fsl_array == NULL) { + free(result); + result = NULL; + goto out; + } + result->locations.locations_len = fsl_count; + result->locations.locations_val = fsl_array; + result->fs_root.pathname4_len = 0; + result->fs_root.pathname4_val = NULL; + + /* + * Copy input, removing escapes from host:/path/to/my\ files + */ + sp = buf; + dp = buf2; + bzero(buf2, sizeof (buf2)); + + while ((sp && *sp && (sp - buf < len)) || gothost) { + + if (!gothost) { + /* Drop leading spaces */ + if (*sp == ' ') { + sp++; + continue; + } + + /* Look for the rightmost colon for host */ + p = strrchr(sp, ':'); + if (!p) { +#ifdef DEBUG + printf("get_fs_locations: skipping %s\n", sp); +#endif + sp += strlen(sp) + 1; + } else { + bcopy(sp, dp, p - sp); + sp = p + 1; +#ifdef DEBUG + printf("get_fs_locations: host %s\n", buf2); +#endif + fsl_array[i].server.server_len = 1; + fsl_array[i].server.server_val = + malloc(sizeof (utf8string)); + if (fsl_array[i].server.server_val == NULL) { + int j; + + free(result); + result = NULL; + for (j = 0; j < i; j++) + free(fsl_array[j]. + server.server_val); + free(fsl_array); + goto out; + } + str_to_utf8(buf2, + fsl_array[i].server.server_val); + gothost = 1; + dp = buf2; + bzero(buf2, sizeof (buf2)); + } + continue; + } + + /* End of string should mean a pathname */ + if (*sp == '\0' && gothost) { +#ifdef DEBUG + printf("get_fs_locations: path %s\n", buf2); +#endif + (void) make_pathname4(buf2, &fsl_array[i].rootpath); + i++; + gothost = 0; + dp = buf2; + bzero(buf2, sizeof (buf2)); + if (sp - buf < len) + sp++; + continue; + } + + /* Skip a single escape character */ + if (*sp == '\\') + sp++; + + /* Plain char, just copy it */ + *dp++ = *sp++; + } + +out: + return (result); +} + +/* + * Deref function for nfs-basic service type returns an fs_locations4. + */ +int +nfs_basic_deref(const char *svc_type, const char *svc_data, char *buf, + size_t *bufsz) +{ + int slen, err; + fs_locations4 *fsl; + XDR xdr; + + if ((!svc_type) || (!svc_data) || (!buf) || (!bufsz) || (*bufsz == 0)) + return (EINVAL); + + if (strcasecmp(svc_type, SERVICE_TYPE)) + return (ENOTSUP); + + fsl = get_fs_locations((char *)svc_data); + if (fsl == NULL) + return (ENOENT); +#ifdef DEBUG + printf("nfs_basic_deref: past get_fs_locations()\n"); +#endif + slen = xdr_sizeof(xdr_fs_locations4, (void *)fsl); + if (slen > *bufsz) { + *bufsz = slen; + xdr_free(xdr_fs_locations4, (char *)fsl); + return (EOVERFLOW); + } +#ifdef DEBUG + printf("nfs_basic_deref: past buffer check\n"); + print_referral_summary(fsl); +#endif + xdrmem_create(&xdr, buf, *bufsz, XDR_ENCODE); + err = xdr_fs_locations4(&xdr, fsl); + XDR_DESTROY(&xdr); + xdr_free(xdr_fs_locations4, (char *)fsl); + if (err != TRUE) + return (EINVAL); + *bufsz = slen; +#ifdef DEBUG + printf("nfs_basic_deref: past xdr_fs_locations4() and done\n"); +#endif + return (0); +} + +/* + * Form function for nfs-basic service type. + */ +int +nfs_basic_form(const char *svc_type, const char *svc_data, char *buf, + size_t *bufsz) +{ + int slen; + + if ((!svc_type) || (!svc_data) || (!buf) || (*bufsz == 0)) + return (EINVAL); + + if (strcmp(svc_type, SERVICE_TYPE)) + return (ENOTSUP); + + slen = strlen(svc_data) + 1; + if (slen > *bufsz) { + *bufsz = slen; + return (EOVERFLOW); + } + *bufsz = slen; + strncpy(buf, svc_data, slen); + return (0); +} diff --git a/usr/src/cmd/fs.d/nfs/rp_basic/mapfile-vers b/usr/src/cmd/fs.d/nfs/rp_basic/mapfile-vers new file mode 100644 index 0000000000..7a1e01b707 --- /dev/null +++ b/usr/src/cmd/fs.d/nfs/rp_basic/mapfile-vers @@ -0,0 +1,46 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# MAPFILE HEADER START +# +# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. +# Object versioning must comply with the rules detailed in +# +# usr/src/lib/README.mapfiles +# +# You should not be making modifications here until you've read the most current +# copy of that file. If you need help, contact a gatekeeper for guidance. +# +# MAPFILE HEADER END +# + +SUNWprivate_1.1 { + global: + rp_plugin_ops; + local: + *; +}; + diff --git a/usr/src/cmd/fs.d/nfs/rp_basic/sparc/Makefile b/usr/src/cmd/fs.d/nfs/rp_basic/sparc/Makefile new file mode 100644 index 0000000000..8673a37a6e --- /dev/null +++ b/usr/src/cmd/fs.d/nfs/rp_basic/sparc/Makefile @@ -0,0 +1,27 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) diff --git a/usr/src/cmd/fs.d/nfs/rp_basic/sparcv9/Makefile b/usr/src/cmd/fs.d/nfs/rp_basic/sparcv9/Makefile new file mode 100644 index 0000000000..f013cacc18 --- /dev/null +++ b/usr/src/cmd/fs.d/nfs/rp_basic/sparcv9/Makefile @@ -0,0 +1,28 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com +include $(SRC)/lib/Makefile.lib.64 + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/cmd/fs.d/nfs/svc/server.xml b/usr/src/cmd/fs.d/nfs/svc/server.xml index e709cc8b36..4a8fac519c 100644 --- a/usr/src/cmd/fs.d/nfs/svc/server.xml +++ b/usr/src/cmd/fs.d/nfs/svc/server.xml @@ -94,6 +94,13 @@ <service_fmri value='svc:/network/shares/group' /> </dependency> + <dependency name='reparse' + grouping='optional_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/system/filesystem/reparse' /> + </dependency> + <!-- Must have all local filesystems mounted before we share them --> <dependency name='filesystem-local' grouping='require_all' diff --git a/usr/src/head/rpcsvc/daemon_utils.h b/usr/src/head/rpcsvc/daemon_utils.h index 8ba538fe81..50611a9579 100644 --- a/usr/src/head/rpcsvc/daemon_utils.h +++ b/usr/src/head/rpcsvc/daemon_utils.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _DAEMON_UTILS_H #define _DAEMON_UTILS_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/stat.h> #ifdef __cplusplus @@ -43,6 +40,7 @@ extern "C" { #define NFS4CBD "svc:/network/nfs/cbd:default" #define NFSMAPID "svc:/network/nfs/mapid:default" #define RQUOTAD "svc:/network/nfs/rquota:default" +#define REPARSED "svc:/system/filesystem/reparse:default" #define DAEMON_UID 1 #define DAEMON_GID 12 diff --git a/usr/src/head/rpcsvc/nfs4_prot.x b/usr/src/head/rpcsvc/nfs4_prot.x index 770784fa10..0a218a6a36 100644 --- a/usr/src/head/rpcsvc/nfs4_prot.x +++ b/usr/src/head/rpcsvc/nfs4_prot.x @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -28,8 +27,6 @@ * nfs4_prot.x */ -%#pragma ident "@(#)nfs4_prot.x 1.122" - /* * Basic typedefs for RFC 1832 data type definitions */ @@ -211,6 +208,16 @@ struct fs_locations4 { fs_location4 locations<>; }; +struct nfs_fsl_info { + uint_t netbuf_len; + uint_t netnm_len; + uint_t knconf_len; + string netname<MAXNETNAMELEN>; + struct netbuf *addr; + struct knetconfig *knconf; +}; + + /* * Various Access Control Entry definitions */ diff --git a/usr/src/lib/libshare/nfs/libshare_nfs.c b/usr/src/lib/libshare/nfs/libshare_nfs.c index 4286f18d22..dc6e5f61d4 100644 --- a/usr/src/lib/libshare/nfs/libshare_nfs.c +++ b/usr/src/lib/libshare/nfs/libshare_nfs.c @@ -114,9 +114,10 @@ struct sa_plugin_ops sa_plugin_ops = { */ static char *service_list_default[] = - { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NULL }; + { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, REPARSED, NULL }; static char *service_list_logging[] = - { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NFSLOGD, NULL }; + { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NFSLOGD, REPARSED, + NULL }; /* * option definitions. Make sure to keep the #define for the option @@ -2930,6 +2931,9 @@ restart_service(uint32_t svcs) case SVC_NFSLOGD: service = NFSLOGD; break; + case SVC_REPARSED: + service = REPARSED; + break; default: continue; } diff --git a/usr/src/lib/libshare/nfs/libshare_nfs.h b/usr/src/lib/libshare/nfs/libshare_nfs.h index 3b612b2954..e67d3b49a8 100644 --- a/usr/src/lib/libshare/nfs/libshare_nfs.h +++ b/usr/src/lib/libshare/nfs/libshare_nfs.h @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -97,6 +97,7 @@ struct option_defs { #define SVC_NFSMAPID 0x0020 #define SVC_RQUOTAD 0x0040 #define SVC_NFSLOGD 0x0080 +#define SVC_REPARSED 0x0100 /* * place holder for future service -- will move to daemon_utils.h when diff --git a/usr/src/pkgdefs/SUNWnfscu/prototype_com b/usr/src/pkgdefs/SUNWnfscu/prototype_com index 0c6c0391db..6ff3f563e0 100644 --- a/usr/src/pkgdefs/SUNWnfscu/prototype_com +++ b/usr/src/pkgdefs/SUNWnfscu/prototype_com @@ -19,11 +19,9 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#pragma ident "%Z%%M% %I% %E% SMI" -# # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package # and their location on the development machine when building the package. diff --git a/usr/src/pkgdefs/SUNWnfssu/prototype_com b/usr/src/pkgdefs/SUNWnfssu/prototype_com index f63bfed138..bd6e0bcf82 100644 --- a/usr/src/pkgdefs/SUNWnfssu/prototype_com +++ b/usr/src/pkgdefs/SUNWnfssu/prototype_com @@ -18,11 +18,9 @@ # # CDDL HEADER END # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package # and their location on the development machine when building the package. @@ -53,5 +51,9 @@ f none usr/lib/nfs/mountd 555 root bin f none usr/lib/nfs/nfsd 555 root bin f none usr/lib/nfs/nfslogd 555 root bin f none usr/lib/nfs/rquotad 555 root bin +d none usr/lib/reparse 755 root bin +f none usr/lib/reparse/libnfs_basic.so.1 755 root bin +s none usr/lib/reparse/libnfs_basic.so=./libnfs_basic.so.1 d none usr/sbin 755 root bin f none usr/sbin/exportfs 555 root bin +f none usr/sbin/nfsref 555 root bin diff --git a/usr/src/pkgdefs/SUNWnfssu/prototype_i386 b/usr/src/pkgdefs/SUNWnfssu/prototype_i386 index a6d0dfdab4..23f31e60c8 100644 --- a/usr/src/pkgdefs/SUNWnfssu/prototype_i386 +++ b/usr/src/pkgdefs/SUNWnfssu/prototype_i386 @@ -18,11 +18,9 @@ # # CDDL HEADER END # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package # and their location on the development machine when building the package. @@ -47,3 +45,6 @@ # d none usr/lib/fs/nfs/amd64 755 root sys f none usr/lib/fs/nfs/amd64/libshare_nfs.so.1 755 root bin +d none usr/lib/reparse/amd64 755 root sys +f none usr/lib/reparse/amd64/libnfs_basic.so.1 755 root bin +s none usr/lib/reparse/amd64/libnfs_basic.so=./libnfs_basic.so.1 diff --git a/usr/src/pkgdefs/SUNWnfssu/prototype_sparc b/usr/src/pkgdefs/SUNWnfssu/prototype_sparc index e4ae96f821..f2f9b32c48 100644 --- a/usr/src/pkgdefs/SUNWnfssu/prototype_sparc +++ b/usr/src/pkgdefs/SUNWnfssu/prototype_sparc @@ -18,11 +18,9 @@ # # CDDL HEADER END # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# # This required package information file contains a list of package contents. # The 'pkgmk' command uses this file to identify the contents of a package # and their location on the development machine when building the package. @@ -46,3 +44,6 @@ # d none usr/lib/fs/nfs/sparcv9 755 root sys f none usr/lib/fs/nfs/sparcv9/libshare_nfs.so.1 755 root bin +d none usr/lib/reparse/sparcv9 755 root sys +f none usr/lib/reparse/sparcv9/libnfs_basic.so.1 755 root bin +s none usr/lib/reparse/sparcv9/libnfs_basic.so=./libnfs_basic.so.1 diff --git a/usr/src/uts/common/fs/fs_subr.c b/usr/src/uts/common/fs/fs_subr.c index a465a97043..b1fec8b4ba 100644 --- a/usr/src/uts/common/fs/fs_subr.c +++ b/usr/src/uts/common/fs/fs_subr.c @@ -938,7 +938,7 @@ int reparse_kderef(const char *svc_type, const char *svc_data, char *buf, size_t *bufsize) { - int err, retries, need_free; + int err, retries, need_free, retried_doorhd; size_t dlen, res_len; char *darg; door_arg_t door_args; @@ -975,6 +975,7 @@ reparse_kderef(const char *svc_type, const char *svc_data, char *buf, door_args.rsize = *bufsize; /* do the door_call */ + retried_doorhd = 0; retries = 0; door_ki_hold(rp_door); while ((err = door_ki_upcall_limited(rp_door, &door_args, @@ -987,10 +988,23 @@ reparse_kderef(const char *svc_type, const char *svc_data, char *buf, } else if (err == EBADF) { /* door server goes away... */ reparse_door_reset_handle(); + + if (retried_doorhd == 0) { + door_ki_rele(rp_door); + retried_doorhd++; + rp_door = reparse_door_get_handle(); + if (rp_door != NULL) { + door_ki_hold(rp_door); + continue; + } + } } break; } - door_ki_rele(rp_door); + + if (rp_door) + door_ki_rele(rp_door); + if (need_free) kmem_free(darg, dlen); /* done with args buffer */ diff --git a/usr/src/uts/common/fs/nfs/nfs3_srv.c b/usr/src/uts/common/fs/nfs/nfs3_srv.c index e060023197..71ebdb2d74 100644 --- a/usr/src/uts/common/fs/nfs/nfs3_srv.c +++ b/usr/src/uts/common/fs/nfs/nfs3_srv.c @@ -112,6 +112,10 @@ rfs3_getattr(GETATTR3args *args, GETATTR3res *resp, struct exportinfo *exi, error = rfs4_delegated_getattr(vp, &va, 0, cr); if (!error) { + /* Lie about the object type for a referral */ + if (vn_is_nfs_reparse(vp, cr)) + va.va_type = VLNK; + /* overflow error if time or size is out of range */ error = vattr_to_fattr3(&va, &resp->resok.obj_attributes); if (error) @@ -792,6 +796,7 @@ rfs3_readlink(READLINK3args *args, READLINK3res *resp, struct exportinfo *exi, char *data; struct sockaddr *ca; char *name = NULL; + int is_referral = 0; vap = NULL; @@ -817,7 +822,11 @@ rfs3_readlink(READLINK3args *args, READLINK3res *resp, struct exportinfo *exi, vap = &va; #endif - if (vp->v_type != VLNK) { + /* We lied about the object type for a referral */ + if (vn_is_nfs_reparse(vp, cr)) + is_referral = 1; + + if (vp->v_type != VLNK && !is_referral) { resp->status = NFS3ERR_INVAL; goto out1; } @@ -845,16 +854,39 @@ rfs3_readlink(READLINK3args *args, READLINK3res *resp, struct exportinfo *exi, data = kmem_alloc(MAXPATHLEN + 1, KM_SLEEP); - iov.iov_base = data; - iov.iov_len = MAXPATHLEN; - uio.uio_iov = &iov; - uio.uio_iovcnt = 1; - uio.uio_segflg = UIO_SYSSPACE; - uio.uio_extflg = UIO_COPY_CACHED; - uio.uio_loffset = 0; - uio.uio_resid = MAXPATHLEN; + if (is_referral) { + char *s; + size_t strsz; + + /* Get an artificial symlink based on a referral */ + s = build_symlink(vp, cr, &strsz); + global_svstat_ptr[3][NFS_REFERLINKS].value.ui64++; + DTRACE_PROBE2(nfs3serv__func__referral__reflink, + vnode_t *, vp, char *, s); + if (s == NULL) + error = EINVAL; + else { + error = 0; + (void) strlcpy(data, s, MAXPATHLEN + 1); + kmem_free(s, strsz); + } - error = VOP_READLINK(vp, &uio, cr, NULL); + } else { + + iov.iov_base = data; + iov.iov_len = MAXPATHLEN; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_extflg = UIO_COPY_CACHED; + uio.uio_loffset = 0; + uio.uio_resid = MAXPATHLEN; + + error = VOP_READLINK(vp, &uio, cr, NULL); + + if (!error) + *(data + MAXPATHLEN - uio.uio_resid) = '\0'; + } #ifdef DEBUG if (rfs3_do_post_op_attr) { @@ -866,6 +898,9 @@ rfs3_readlink(READLINK3args *args, READLINK3res *resp, struct exportinfo *exi, va.va_mask = AT_ALL; vap = VOP_GETATTR(vp, &va, 0, cr, NULL) ? NULL : &va; #endif + /* Lie about object type again just to be consistent */ + if (is_referral && vap != NULL) + vap->va_type = VLNK; #if 0 /* notyet */ /* @@ -884,8 +919,6 @@ rfs3_readlink(READLINK3args *args, READLINK3res *resp, struct exportinfo *exi, goto out; } - *(data + MAXPATHLEN - uio.uio_resid) = '\0'; - ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf; name = nfscmd_convname(ca, exi, data, NFSCMD_CONV_OUTBOUND, MAXPATHLEN + 1); @@ -3863,6 +3896,10 @@ good: nva.va_mask = AT_ALL; nvap = rfs4_delegated_getattr(nvp, &nva, 0, cr) ? NULL : &nva; #endif + /* Lie about the object type for a referral */ + if (vn_is_nfs_reparse(nvp, cr)) + nvap->va_type = VLNK; + vattr_to_post_op_attr(nvap, &infop[i].attr); #ifdef DEBUG diff --git a/usr/src/uts/common/fs/nfs/nfs4_attr.c b/usr/src/uts/common/fs/nfs/nfs4_attr.c index 1740b64d30..c32a9526ec 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_attr.c +++ b/usr/src/uts/common/fs/nfs/nfs4_attr.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. - * All rights reserved. Use is subject to license terms. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/time.h> #include <sys/systm.h> @@ -107,7 +104,7 @@ nfs4_ver_fattr4_attr(vattr_t *vap, struct nfs4_ntov_map *ntovp, case AT_ATIME: if ((ntovp->nval != FATTR4_TIME_ACCESS) || (*errorp = nfs4_time_vton(&vap->va_ctime, - &nap->time_access))) { + &nap->time_access))) { /* * either asked for FATTR4_TIME_ACCESS_SET - * not used for setattr @@ -119,7 +116,7 @@ nfs4_ver_fattr4_attr(vattr_t *vap, struct nfs4_ntov_map *ntovp, case AT_MTIME: if ((ntovp->nval != FATTR4_TIME_MODIFY) || (*errorp = nfs4_time_vton(&vap->va_mtime, - &nap->time_modify))) { + &nap->time_modify))) { /* * either asked for FATTR4_TIME_MODIFY_SET - * not used for setattr @@ -130,7 +127,7 @@ nfs4_ver_fattr4_attr(vattr_t *vap, struct nfs4_ntov_map *ntovp, break; case AT_CTIME: if (*errorp = nfs4_time_vton(&vap->va_ctime, - &nap->time_metadata)) { + &nap->time_metadata)) { /* * system time invalid for otw transfers */ @@ -196,7 +193,7 @@ nfs4_set_fattr4_attr(vattr_t *vap, vsecattr_t *vsap, case AT_ATIME: if ((ntovp->nval != FATTR4_TIME_ACCESS_SET) || (*errorp = timestruc_to_settime4(&vap->va_atime, - &nap->time_access_set, flags))) { + &nap->time_access_set, flags))) { /* FATTR4_TIME_ACCESS - not used for verify */ retval = FALSE; } @@ -204,7 +201,7 @@ nfs4_set_fattr4_attr(vattr_t *vap, vsecattr_t *vsap, case AT_MTIME: if ((ntovp->nval != FATTR4_TIME_MODIFY_SET) || (*errorp = timestruc_to_settime4(&vap->va_mtime, - &nap->time_modify_set, flags))) { + &nap->time_modify_set, flags))) { /* FATTR4_TIME_MODIFY - not used for verify */ retval = FALSE; } @@ -260,7 +257,7 @@ vattr_to_fattr4(vattr_t *vap, vsecattr_t *vsap, fattr4 *fattrp, int flags, fattrp->attrlist4_len = 0; fattrp->attrlist4 = NULL; na = kmem_zalloc(sizeof (union nfs4_attr_u) * nfs4_ntov_map_size, - KM_SLEEP); + KM_SLEEP); if (op == OP_SETATTR || op == OP_CREATE || op == OP_OPEN) { /* @@ -341,8 +338,8 @@ vattr_to_fattr4(vattr_t *vap, vsecattr_t *vsap, fattr4 *fattrp, int flags, xdr_size += nfs4_ntov_map[i].xdr_size; if ((nfs4_ntov_map[i].nval == FATTR4_TIME_ACCESS_SET || nfs4_ntov_map[i].nval == FATTR4_TIME_MODIFY_SET) && - attrfunc == nfs4_set_fattr4_attr && - !(flags & ATTR_UTIME)) { + attrfunc == nfs4_set_fattr4_attr && + !(flags & ATTR_UTIME)) { xdr_size -= 3 * BYTES_PER_XDR_UNIT; } } else { @@ -351,19 +348,19 @@ vattr_to_fattr4(vattr_t *vap, vsecattr_t *vsap, fattr4 *fattrp, int flags, * are AT_UID, AT_GID and FATTR4_ACL_MASK */ ASSERT(nfs4_ntov_map[i].vbit == AT_UID || - nfs4_ntov_map[i].vbit == AT_GID || - nfs4_ntov_map[i].fbit == FATTR4_ACL_MASK); + nfs4_ntov_map[i].vbit == AT_GID || + nfs4_ntov_map[i].fbit == FATTR4_ACL_MASK); if (nfs4_ntov_map[i].vbit == AT_UID) { uid_attr = attrcnt; xdr_size += BYTES_PER_XDR_UNIT; /* length */ xdr_size += - RNDUP(na[attrcnt].owner.utf8string_len); + RNDUP(na[attrcnt].owner.utf8string_len); } else if (nfs4_ntov_map[i].vbit == AT_GID) { gid_attr = attrcnt; xdr_size += BYTES_PER_XDR_UNIT; /* length */ xdr_size += RNDUP( - na[attrcnt].owner_group.utf8string_len); + na[attrcnt].owner_group.utf8string_len); } else if (nfs4_ntov_map[i].fbit == FATTR4_ACL_MASK) { nfsace4 *tmpacl = (nfsace4 *)vsap->vsa_aclentp; @@ -411,7 +408,7 @@ vattr_to_fattr4(vattr_t *vap, vsecattr_t *vsap, fattr4 *fattrp, int flags, for (i = 0; i < attrcnt; i++) { if ((*nfs4_ntov_map[amap[i]].xfunc)(&xdr, &na[i]) == FALSE) { cmn_err(CE_WARN, "vattr_to_fattr4: xdr encode of " - "attribute failed\n"); + "attribute failed\n"); error = EINVAL; break; } @@ -422,11 +419,11 @@ done: */ if (uid_attr != -1 && na[uid_attr].owner.utf8string_val != NULL) { kmem_free(na[uid_attr].owner.utf8string_val, - na[uid_attr].owner.utf8string_len); + na[uid_attr].owner.utf8string_len); } if (gid_attr != -1 && na[gid_attr].owner_group.utf8string_val != NULL) { kmem_free(na[gid_attr].owner_group.utf8string_val, - na[gid_attr].owner_group.utf8string_len); + na[gid_attr].owner_group.utf8string_len); } /* xdrmem_destroy(&xdrs); */ /* NO-OP */ @@ -770,7 +767,7 @@ struct nfs4_ntov_map nfs4_ntov_map[] = { FATTR4_TIME_MODIFY_SET, 4 * BYTES_PER_XDR_UNIT, xdr_settime4, NULL, "fattr4_time_modify_set" }, - { FATTR4_MOUNTED_ON_FILEID_MASK, 0, FALSE, FALSE, + { FATTR4_MOUNTED_ON_FILEID_MASK, AT_NODEID, FALSE, FALSE, FATTR4_MOUNTED_ON_FILEID, 2 * BYTES_PER_XDR_UNIT, xdr_u_longlong_t, NULL, "fattr4_mounted_on_fileid" }, diff --git a/usr/src/uts/common/fs/nfs/nfs4_callback.c b/usr/src/uts/common/fs/nfs/nfs4_callback.c index 057eb3c608..90c570ba24 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_callback.c +++ b/usr/src/uts/common/fs/nfs/nfs4_callback.c @@ -1493,7 +1493,8 @@ nfs4_do_delegreturn(rnode4_t *rp, int flags, cred_t *cr, (void) nfs4_start_recovery(&e, mi, vp, NULL, &rp->r_deleg_stateid, lost_rqst.lr_op == OP_DELEGRETURN ? - &lost_rqst : NULL, OP_DELEGRETURN, NULL); + &lost_rqst : NULL, OP_DELEGRETURN, NULL, + NULL, NULL); nfs4_end_op(mi, vp, NULL, &recov_state, needrecov); break; } @@ -1517,7 +1518,8 @@ nfs4_do_delegreturn(rnode4_t *rp, int flags, cred_t *cr, (void) nfs4_start_recovery(&e, mi, vp, NULL, &rp->r_deleg_stateid, lost_rqst.lr_op == OP_DELEGRETURN ? - &lost_rqst : NULL, OP_DELEGRETURN, NULL); + &lost_rqst : NULL, OP_DELEGRETURN, NULL, + NULL, NULL); } else { nfs4delegreturn_cleanup_impl(rp, NULL, ncg); done = TRUE; @@ -1956,7 +1958,7 @@ retry: * thread will take it from here. */ (void) nfs4_start_recovery(&e, mi, vp, NULL, NULL, - NULL, OP_OPEN, NULL); + NULL, OP_OPEN, NULL, NULL, NULL); open_stream_rele(osp, rp); *recovp = TRUE; break; diff --git a/usr/src/uts/common/fs/nfs/nfs4_client.c b/usr/src/uts/common/fs/nfs/nfs4_client.c index b60b211df1..abbdaebee7 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_client.c +++ b/usr/src/uts/common/fs/nfs/nfs4_client.c @@ -837,7 +837,7 @@ recov_retry: if (nfs4_needs_recovery(&e, FALSE, vp->v_vfsp)) { if (nfs4_start_recovery(&e, VTOMI4(vp), vp, NULL, NULL, - NULL, OP_GETATTR, NULL) == FALSE) { + NULL, OP_GETATTR, NULL, NULL, NULL) == FALSE) { nfs4_end_fop(VTOMI4(vp), vp, NULL, OH_GETATTR, &recov_state, 1); goto recov_retry; @@ -1027,7 +1027,7 @@ recov_retry: "nfs4_attr_otw: initiating recovery\n")); abort = nfs4_start_recovery(&e, VTOMI4(vp), vp, NULL, NULL, - NULL, OP_GETATTR, NULL); + NULL, OP_GETATTR, NULL, NULL, NULL); nfs4_end_fop(VTOMI4(vp), vp, NULL, OH_GETATTR, &recov_state, needrecov); if (!e.error) { @@ -3492,7 +3492,7 @@ recov_retry: "nfs4renew: initiating recovery\n")); if (nfs4_start_recovery(&e, mi, NULL, NULL, NULL, NULL, - OP_RENEW, NULL) == FALSE) { + OP_RENEW, NULL, NULL, NULL) == FALSE) { nfs4_end_op(mi, NULL, NULL, &recov_state, needrecov); VFS_RELE(mi->mi_vfsp); if (!e.error) diff --git a/usr/src/uts/common/fs/nfs/nfs4_client_debug.c b/usr/src/uts/common/fs/nfs/nfs4_client_debug.c index 5336ddf0c8..b8e85ae198 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_client_debug.c +++ b/usr/src/uts/common/fs/nfs/nfs4_client_debug.c @@ -303,6 +303,17 @@ set_event(nfs4_event_type_t id, nfs4_revent_t *ep, mntinfo4_t *mi, if (rp2 != NULL && rp2->r_svnode.sv_name != NULL) ep->re_char2 = fn_path(rp2->r_svnode.sv_name); break; + case RE_REFERRAL: + /* server we're being referred to */ + if (server1 != NULL) { + len = strlen(server1); + ep->re_char1 = kmem_alloc(len + 1, KM_SLEEP); + bcopy(server1, ep->re_char1, len); + ep->re_char1[len] = '\0'; + } else { + ep->re_char1 = NULL; + } + break; default: break; } @@ -391,6 +402,8 @@ successful_comm(nfs4_debug_msg_t *msgp) case RE_SIGLOST: case RE_SIGLOST_NO_DUMP: case RE_LOST_STATE_BAD_OP: + case RE_REFERRAL: + /* placeholder */ return (0); default: return (0); @@ -1039,6 +1052,18 @@ queue_print_event(nfs4_debug_msg_t *msg, mntinfo4_t *mi, int dump) ep->re_char1, (void *)ep->re_rp1, ep->re_char2, (void *)ep->re_rp2); break; + case RE_REFERRAL: + if (ep->re_char1) + zcmn_err(zoneid, CE_NOTE, + "![NFS4][Server: %s][Mntpt: %s]" + "being referred from %s to %s", msg->msg_srv, + msg->msg_mntpt, msg->msg_srv, ep->re_char1); + else + zcmn_err(zoneid, CE_NOTE, + "![NFS4][Server: %s][Mntpt: %s]" + "NFS4: being referred from %s to unknown server", + msg->msg_srv, msg->msg_mntpt, msg->msg_srv); + break; default: zcmn_err(zoneid, CE_WARN, "!queue_print_event: illegal event %d", ep->re_type); @@ -1186,6 +1211,7 @@ id_to_dump_solo_event(nfs4_event_type_t id) case RE_UNEXPECTED_ERRNO: case RE_UNEXPECTED_STATUS: case RE_LOST_STATE_BAD_OP: + case RE_REFERRAL: return (1); default: return (0); diff --git a/usr/src/uts/common/fs/nfs/nfs4_client_secinfo.c b/usr/src/uts/common/fs/nfs/nfs4_client_secinfo.c index 3546a88ea9..ed2c2c167a 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_client_secinfo.c +++ b/usr/src/uts/common/fs/nfs/nfs4_client_secinfo.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * NFS Version 4 client side SECINFO code. */ @@ -749,7 +747,7 @@ retry: "nfs4secinfo_otw: recovery in a recovery thread\n")); abort = nfs4_start_recovery(&e, mi, NULL, - NULL, NULL, NULL, OP_SECINFO, NULL); + NULL, NULL, NULL, OP_SECINFO, NULL, NULL, NULL); if (!e.error) { e.error = geterrno4(res.status); (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); diff --git a/usr/src/uts/common/fs/nfs/nfs4_common.c b/usr/src/uts/common/fs/nfs/nfs4_common.c index 4966d48946..fbd2670acc 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_common.c +++ b/usr/src/uts/common/fs/nfs/nfs4_common.c @@ -19,13 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - - /* * This is the loadable module wrapper. */ @@ -164,28 +161,28 @@ nfs4_setopts(vnode_t *vp, model_t model, struct nfs_args *buf) mi->mi_acregmin = SEC2HR(ACMINMAX); else mi->mi_acregmin = SEC2HR(MIN(STRUCT_FGET(args, - acregmin), ACMINMAX)); + acregmin), ACMINMAX)); } if (flags & NFSMNT_ACREGMAX) { if (STRUCT_FGET(args, acregmax) < 0) mi->mi_acregmax = SEC2HR(ACMAXMAX); else mi->mi_acregmax = SEC2HR(MIN(STRUCT_FGET(args, - acregmax), ACMAXMAX)); + acregmax), ACMAXMAX)); } if (flags & NFSMNT_ACDIRMIN) { if (STRUCT_FGET(args, acdirmin) < 0) mi->mi_acdirmin = SEC2HR(ACMINMAX); else mi->mi_acdirmin = SEC2HR(MIN(STRUCT_FGET(args, - acdirmin), ACMINMAX)); + acdirmin), ACMINMAX)); } if (flags & NFSMNT_ACDIRMAX) { if (STRUCT_FGET(args, acdirmax) < 0) mi->mi_acdirmax = SEC2HR(ACMAXMAX); else mi->mi_acdirmax = SEC2HR(MIN(STRUCT_FGET(args, - acdirmax), ACMAXMAX)); + acdirmax), ACMAXMAX)); } return (0); @@ -437,6 +434,8 @@ nfs4_recov_action_to_str(nfs4_recov_t what) return ("NR_LOST_LOCK"); case NR_LOST_STATE_RQST: return ("NR_LOST_STATE_RQST"); + case NR_MOVED: + return ("NR_MOVED"); default: (void) snprintf(buf, 40, "Unknown, code %d", (int)what); return (buf); diff --git a/usr/src/uts/common/fs/nfs/nfs4_idmap.c b/usr/src/uts/common/fs/nfs/nfs4_idmap.c index 14e1708e53..a5f05a5d34 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_idmap.c +++ b/usr/src/uts/common/fs/nfs/nfs4_idmap.c @@ -19,11 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" /* * There are well defined policies for mapping uid and gid values to and @@ -134,7 +133,7 @@ /* * Truly global modular globals */ -static zone_key_t nfsidmap_zone_key; +zone_key_t nfsidmap_zone_key; static list_t nfsidmap_globals_list; static kmutex_t nfsidmap_globals_lock; static kmem_cache_t *nfsidmap_cache; diff --git a/usr/src/uts/common/fs/nfs/nfs4_recovery.c b/usr/src/uts/common/fs/nfs/nfs4_recovery.c index 9d2ef14175..ca32816d19 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_recovery.c +++ b/usr/src/uts/common/fs/nfs/nfs4_recovery.c @@ -39,6 +39,11 @@ #include <sys/disp.h> #include <sys/list.h> #include <sys/sdt.h> +#include <sys/mount.h> +#include <sys/door.h> +#include <nfs/nfssys.h> +#include <nfs/nfsid_map.h> +#include <nfs/nfs4_idmap_impl.h> extern r4hashq_t *rtable4; @@ -69,6 +74,8 @@ typedef struct { nfs4_error_t rc_orig_errors; /* original errors causing recovery */ int rc_error; nfs4_bseqid_entry_t *rc_bseqid_rqst; + vnode_t *rc_moved_vp; + char *rc_moved_nm; } recov_info_t; /* @@ -135,6 +142,8 @@ int nfs4_srvmnt_fail_cnt = 0; int nfs4_srvmnt_debug = 0; #endif +extern zone_key_t nfs4clnt_zone_key; + /* forward references, in alphabetic order */ static void close_after_open_resend(vnode_t *, cred_t *, uint32_t, nfs4_error_t *); @@ -169,7 +178,7 @@ static void resend_one_op(nfs4_lost_rqst_t *, nfs4_error_t *, mntinfo4_t *, nfs4_server_t *); static void save_bseqid_rqst(nfs4_bseqid_entry_t *, recov_info_t *); static void start_recovery(recov_info_t *, mntinfo4_t *, vnode_t *, vnode_t *, - nfs4_server_t *); + nfs4_server_t *, vnode_t *, char *); static void start_recovery_action(nfs4_recov_t, bool_t, mntinfo4_t *, vnode_t *, vnode_t *); static int wait_for_recovery(mntinfo4_t *, nfs4_op_hint_t); @@ -330,7 +339,7 @@ enqueue_bseqid_rqst(recov_info_t *recovp, mntinfo4_t *mi) bool_t nfs4_start_recovery(nfs4_error_t *ep, mntinfo4_t *mi, vnode_t *vp1, vnode_t *vp2, stateid4 *sid, nfs4_lost_rqst_t *lost_rqstp, nfs_opnum4 op, - nfs4_bseqid_entry_t *bsep) + nfs4_bseqid_entry_t *bsep, vnode_t *moved_vp, char *moved_nm) { recov_info_t *recovp; nfs4_server_t *sp; @@ -371,7 +380,7 @@ nfs4_start_recovery(nfs4_error_t *ep, mntinfo4_t *mi, vnode_t *vp1, errs_to_action(recovp, sp, mi, sid, lost_rqstp, gone, op, bsep); if (sp != NULL) mutex_exit(&sp->s_lock); - start_recovery(recovp, mi, vp1, vp2, sp); + start_recovery(recovp, mi, vp1, vp2, sp, moved_vp, moved_nm); if (sp != NULL) nfs4_server_rele(sp); return (FALSE); @@ -397,12 +406,13 @@ start_recovery_action(nfs4_recov_t what, bool_t reboot, mntinfo4_t *mi, recovp->rc_action = what; recovp->rc_srv_reboot = reboot; recovp->rc_error = EIO; - start_recovery(recovp, mi, vp1, vp2, NULL); + start_recovery(recovp, mi, vp1, vp2, NULL, NULL, NULL); } static void start_recovery(recov_info_t *recovp, mntinfo4_t *mi, - vnode_t *vp1, vnode_t *vp2, nfs4_server_t *sp) + vnode_t *vp1, vnode_t *vp2, nfs4_server_t *sp, + vnode_t *moved_vp, char *moved_nm) { NFS4_DEBUG(nfs4_client_recov_debug, (CE_NOTE, "start_recovery: mi %p, what %s", (void*)mi, @@ -563,7 +573,6 @@ again: case NR_LOST_LOCK: nfs4_enqueue_lost_rqst(recovp, mi); break; - default: nfs4_queue_event(RE_UNEXPECTED_ACTION, mi, NULL, recovp->rc_action, NULL, NULL, 0, NULL, 0, TAG_NONE, @@ -607,6 +616,8 @@ again: ASSERT(VTOMI4(vp2) == mi); VN_HOLD(recovp->rc_vp2); } + recovp->rc_moved_vp = moved_vp; + recovp->rc_moved_nm = moved_nm; (void) zthread_create(NULL, 0, nfs4_recov_thread, recovp, 0, minclsyspri); @@ -1937,7 +1948,7 @@ recov_filehandle(nfs4_recov_t action, mntinfo4_t *mi, vnode_t *vp) needrecov = FALSE; if (needrecov) { (void) nfs4_start_recovery(&e, mi, vp, - NULL, NULL, NULL, OP_LOOKUP, NULL); + NULL, NULL, NULL, OP_LOOKUP, NULL, NULL, NULL); } else if (e.error != EINTR && !NFS4_FRC_UNMT_ERR(e.error, mi->mi_vfsp) && (e.error != 0 || e.stat != NFS4_OK)) { @@ -2012,7 +2023,7 @@ recov_stale(mntinfo4_t *mi, vnode_t *vp) needrecov = nfs4_needs_recovery(&e, FALSE, vp->v_vfsp); if (needrecov && (e.error != 0 || e.stat != NFS4ERR_STALE)) { (void) nfs4_start_recovery(&e, mi, vp, - NULL, NULL, NULL, OP_GETATTR, NULL); + NULL, NULL, NULL, OP_GETATTR, NULL, NULL, NULL); NFS4_DEBUG(nfs4_client_recov_debug, (CE_NOTE, "recov_stale: error=%d, stat=%d seen on rp %s", e.error, e.stat, rnode4info(rp))); @@ -2062,7 +2073,7 @@ recov_stale(mntinfo4_t *mi, vnode_t *vp) if (needrecov) { (void) nfs4_start_recovery(&e, mi, rootvp, NULL, NULL, NULL, - OP_GETATTR, NULL); + OP_GETATTR, NULL, NULL, NULL); NFS4_DEBUG(nfs4_client_recov_debug, (CE_NOTE, "recov_stale: error=%d, stat=%d seen " "on rp %s", e.error, e.stat, @@ -2491,7 +2502,7 @@ recov_openfiles(recov_info_t *recovp, nfs4_server_t *sp) nfs4_remap_root(mi, &e, 0); if (nfs4_needs_recovery(&e, FALSE, mi->mi_vfsp)) { (void) nfs4_start_recovery(&e, mi, NULL, - NULL, NULL, NULL, OP_LOOKUP, NULL); + NULL, NULL, NULL, OP_LOOKUP, NULL, NULL, NULL); } } @@ -2561,7 +2572,7 @@ recov_openfiles(recov_info_t *recovp, nfs4_server_t *sp) mi->mi_vfsp)) { (void) nfs4_start_recovery(&e, mi, rep->re_vp, NULL, NULL, NULL, - OP_OPEN, NULL); + OP_OPEN, NULL, NULL, NULL); break; } } @@ -2575,7 +2586,7 @@ recov_openfiles(recov_info_t *recovp, nfs4_server_t *sp) if (nfs4_needs_recovery(&e, TRUE, mi->mi_vfsp)) (void) nfs4_start_recovery(&e, mi, rep->re_vp, NULL, NULL, NULL, OP_LOCK, - NULL); + NULL, NULL, NULL); if (e.error != 0 || e.stat != NFS4_OK) break; } @@ -2664,7 +2675,7 @@ nfs4_resend_lost_rqsts(recov_info_t *recovp, nfs4_server_t *sp) } else { (void) nfs4_start_recovery(&n4e, mi, lrp->lr_dvp, lrp->lr_vp, NULL, NULL, - lrp->lr_op, NULL); + lrp->lr_op, NULL, NULL, NULL); } return; } @@ -3122,10 +3133,10 @@ errs_to_action(recov_info_t *recovp, case NFS4ERR_LEASE_MOVED: action = xxx; break; +#endif case NFS4ERR_MOVED: - action = xxx; + action = NR_MOVED; break; -#endif case NFS4ERR_BADHANDLE: action = NR_BADHANDLE; break; diff --git a/usr/src/uts/common/fs/nfs/nfs4_rnode.c b/usr/src/uts/common/fs/nfs/nfs4_rnode.c index 14cb143e95..48e9eafbb0 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_rnode.c +++ b/usr/src/uts/common/fs/nfs/nfs4_rnode.c @@ -381,11 +381,12 @@ r4_do_attrcache(vnode_t *vp, nfs4_ga_res_t *garp, int newnode, * creation time and it never changes for life * of the rnode. * + * This stub will be for a mirror-mount, rather than + * a referral (the latter also sets R4SRVSTUB). + * * The stub type is also set during RO failover, * nfs4_remap_file(). * - * This stub will be for a mirror-mount. - * * We don't bother with taking r_state_lock to * set the stub type because this is a new rnode * and we're holding the hash bucket r_lock RW_WRITER. @@ -1769,6 +1770,52 @@ r4mkopenlist(mntinfo4_t *mi) } /* + * Given a filesystem id, check to see if any rnodes + * within this fsid reside in the rnode cache, other + * than one we know about. + * + * Return 1 if an rnode is found, 0 otherwise + */ +int +r4find_by_fsid(mntinfo4_t *mi, fattr4_fsid *moved_fsid) +{ + rnode4_t *rp; + vnode_t *vp; + vfs_t *vfsp = mi->mi_vfsp; + fattr4_fsid *fsid; + int index, found = 0; + + for (index = 0; index < rtable4size; index++) { + rw_enter(&rtable4[index].r_lock, RW_READER); + for (rp = rtable4[index].r_hashf; + rp != (rnode4_t *)(&rtable4[index]); + rp = rp->r_hashf) { + + vp = RTOV4(rp); + if (vp->v_vfsp != vfsp) + continue; + + /* + * XXX there might be a case where a + * replicated fs may have the same fsid + * across two different servers. This + * check isn't good enough in that case + */ + fsid = &rp->r_srv_fsid; + if (FATTR4_FSID_EQ(moved_fsid, fsid)) { + found = 1; + break; + } + } + rw_exit(&rtable4[index].r_lock); + + if (found) + break; + } + return (found); +} + +/* * Release the list of open instance references. */ @@ -1898,6 +1945,14 @@ r4_stub_mirrormount(rnode4_t *rp) } void +r4_stub_referral(rnode4_t *rp) +{ + DTRACE_PROBE1(nfs4clnt__func__referral__moved, + vnode_t *, RTOV4(rp)); + r4_stub_set(rp, NFS4_STUB_REFERRAL); +} + +void r4_stub_none(rnode4_t *rp) { r4_stub_set(rp, NFS4_STUB_NONE); diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv.c b/usr/src/uts/common/fs/nfs/nfs4_srv.c index 842c30d53e..c7163712c0 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_srv.c +++ b/usr/src/uts/common/fs/nfs/nfs4_srv.c @@ -56,6 +56,8 @@ #include <sys/ddi.h> #include <sys/zone.h> +#include <fs/fs_reparse.h> + #include <rpc/types.h> #include <rpc/auth.h> #include <rpc/rpcsec_gss.h> @@ -82,7 +84,6 @@ static int rfs4_maxlock_tries = RFS4_MAXLOCK_TRIES; #define RFS4_LOCK_DELAY 10 /* Milliseconds */ static clock_t rfs4_lock_delay = RFS4_LOCK_DELAY; extern struct svc_ops rdma_svc_ops; -/* End of Tunables */ static int rdma_setup_read_data4(READ4args *, READ4res *); @@ -243,7 +244,7 @@ static void rfs4_op_secinfo_free(nfs_resop4 *); static nfsstat4 check_open_access(uint32_t, struct compound_state *, struct svc_req *); nfsstat4 rfs4_client_sysid(rfs4_client_t *, sysid_t *); -void rfs4_ss_clid(rfs4_client_t *, struct svc_req *); +void rfs4_ss_clid(rfs4_client_t *); /* * translation table for attrs @@ -465,6 +466,8 @@ void rfs4_ss_chkclid(rfs4_client_t *); extern size_t strlcpy(char *dst, const char *src, size_t dstsize); +extern void rfs4_free_fs_locations4(fs_locations4 *); + #ifdef nextdp #undef nextdp #endif @@ -1609,6 +1612,7 @@ rfs4_op_create(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, resp->attrset = 0; sarg.sbp = &sb; + sarg.is_referral = B_FALSE; nfs4_ntov_table_init(&ntov); status = do_rfs4_set_attrs(&resp->attrset, @@ -2314,13 +2318,26 @@ rfs4_op_getattr(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, sarg.sbp = &sb; sarg.cs = cs; + sarg.is_referral = B_FALSE; status = bitmap4_to_attrmask(args->attr_request, &sarg); if (status == NFS4_OK) { + status = bitmap4_get_sysattrs(&sarg); - if (status == NFS4_OK) + if (status == NFS4_OK) { + + /* Is this a referral? */ + if (vn_is_nfs_reparse(cs->vp, cs->cr)) { + /* Older V4 Solaris client sees a link */ + if (client_is_downrev(req)) + sarg.vap->va_type = VLNK; + else + sarg.is_referral = B_TRUE; + } + status = do_rfs4_op_getattr(args->attr_request, &resp->obj_attributes, &sarg); + } } *cs->statusp = resp->status = status; out: @@ -2354,6 +2371,25 @@ rfs4_op_getfh(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, goto out; } + /* check for reparse point at the share point */ + if (cs->exi->exi_moved || vn_is_nfs_reparse(cs->exi->exi_vp, cs->cr)) { + /* it's all bad */ + cs->exi->exi_moved = 1; + *cs->statusp = resp->status = NFS4ERR_MOVED; + DTRACE_PROBE2(nfs4serv__func__referral__shared__moved, + vnode_t *, cs->vp, char *, "rfs4_op_getfh"); + return; + } + + /* check for reparse point at vp */ + if (vn_is_nfs_reparse(cs->vp, cs->cr) && !client_is_downrev(req)) { + /* it's not all bad */ + *cs->statusp = resp->status = NFS4ERR_MOVED; + DTRACE_PROBE2(nfs4serv__func__referral__moved, + vnode_t *, cs->vp, char *, "rfs4_op_getfh"); + return; + } + resp->object.nfs_fh4_val = kmem_alloc(cs->fh.nfs_fh4_len, KM_SLEEP); nfs_fh4_copy(&cs->fh, &resp->object); @@ -3688,6 +3724,7 @@ rfs4_op_readlink(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, char *data; struct sockaddr *ca; char *name = NULL; + int is_referral; DTRACE_NFSV4_1(op__readlink__start, struct compound_state *, cs); @@ -3703,14 +3740,25 @@ rfs4_op_readlink(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, goto out; } - if (vp->v_type == VDIR) { - *cs->statusp = resp->status = NFS4ERR_ISDIR; - goto out; - } + /* Is it a referral? */ + if (vn_is_nfs_reparse(vp, cs->cr) && client_is_downrev(req)) { + + is_referral = 1; + + } else { + + is_referral = 0; + + if (vp->v_type == VDIR) { + *cs->statusp = resp->status = NFS4ERR_ISDIR; + goto out; + } + + if (vp->v_type != VLNK) { + *cs->statusp = resp->status = NFS4ERR_INVAL; + goto out; + } - if (vp->v_type != VLNK) { - *cs->statusp = resp->status = NFS4ERR_INVAL; - goto out; } va.va_mask = AT_MODE; @@ -3727,16 +3775,39 @@ rfs4_op_readlink(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, data = kmem_alloc(MAXPATHLEN + 1, KM_SLEEP); - iov.iov_base = data; - iov.iov_len = MAXPATHLEN; - uio.uio_iov = &iov; - uio.uio_iovcnt = 1; - uio.uio_segflg = UIO_SYSSPACE; - uio.uio_extflg = UIO_COPY_CACHED; - uio.uio_loffset = 0; - uio.uio_resid = MAXPATHLEN; + if (is_referral) { + char *s; + size_t strsz; + + /* Get an artificial symlink based on a referral */ + s = build_symlink(vp, cs->cr, &strsz); + global_svstat_ptr[4][NFS_REFERLINKS].value.ui64++; + DTRACE_PROBE2(nfs4serv__func__referral__reflink, + vnode_t *, vp, char *, s); + if (s == NULL) + error = EINVAL; + else { + error = 0; + (void) strlcpy(data, s, MAXPATHLEN + 1); + kmem_free(s, strsz); + } + + } else { + + iov.iov_base = data; + iov.iov_len = MAXPATHLEN; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_extflg = UIO_COPY_CACHED; + uio.uio_loffset = 0; + uio.uio_resid = MAXPATHLEN; + + error = VOP_READLINK(vp, &uio, cs->cr, NULL); - error = VOP_READLINK(vp, &uio, cs->cr, NULL); + if (!error) + *(data + MAXPATHLEN - uio.uio_resid) = '\0'; + } if (error) { kmem_free((caddr_t)data, (uint_t)MAXPATHLEN + 1); @@ -3744,8 +3815,6 @@ rfs4_op_readlink(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, goto out; } - *(data + MAXPATHLEN - uio.uio_resid) = '\0'; - ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf; name = nfscmd_convname(ca, cs->exi, data, NFSCMD_CONV_OUTBOUND, MAXPATHLEN + 1); @@ -5053,6 +5122,7 @@ do_rfs4_op_setattr(bitmap4 *resp, fattr4 *fattrp, struct compound_state *cs, *resp = 0; sarg.sbp = &sb; + sarg.is_referral = B_FALSE; nfs4_ntov_table_init(&ntov); status = do_rfs4_set_attrs(resp, fattrp, cs, &sarg, &ntov, NFS4ATTR_SETIT); @@ -5347,6 +5417,7 @@ rfs4_op_verify(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, } sarg.sbp = &sb; + sarg.is_referral = B_FALSE; nfs4_ntov_table_init(&ntov); resp->status = do_rfs4_set_attrs(NULL, &args->obj_attributes, cs, &sarg, &ntov, NFS4ATTR_VERIT); @@ -5408,6 +5479,7 @@ rfs4_op_nverify(nfs_argop4 *argop, nfs_resop4 *resop, struct svc_req *req, return; } sarg.sbp = &sb; + sarg.is_referral = B_FALSE; nfs4_ntov_table_init(&ntov); resp->status = do_rfs4_set_attrs(NULL, &args->obj_attributes, cs, &sarg, &ntov, NFS4ATTR_VERIT); @@ -6212,6 +6284,7 @@ rfs4_createfile(OPEN4args *args, struct svc_req *req, struct compound_state *cs, char *name = NULL; sarg.sbp = &sb; + sarg.is_referral = B_FALSE; dvp = cs->vp; @@ -7806,7 +7879,8 @@ rfs4_op_setclientid(nfs_argop4 *argop, nfs_resop4 *resop, SETCLIENTID4args *args = &argop->nfs_argop4_u.opsetclientid; SETCLIENTID4res *res = &resop->nfs_resop4_u.opsetclientid; rfs4_client_t *cp, *newcp, *cp_confirmed, *cp_unconfirmed; - bool_t create = TRUE; + rfs4_clntip_t *ci; + bool_t create; char *addr, *netid; int len; @@ -7816,6 +7890,27 @@ retry: newcp = cp_confirmed = cp_unconfirmed = NULL; /* + * Save the caller's IP address + */ + args->client.cl_addr = + (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf; + + /* + * Record if it is a Solaris client that cannot handle referrals. + */ + if (strstr(args->client.id_val, "Solaris") && + !strstr(args->client.id_val, "+referrals")) { + /* Add a "yes, it's downrev" record */ + create = TRUE; + ci = rfs4_find_clntip(args->client.cl_addr, &create); + ASSERT(ci != NULL); + rfs4_dbe_rele(ci->ri_dbe); + } else { + /* Remove any previous record */ + rfs4_invalidate_clntip(args->client.cl_addr); + } + + /* * In search of an EXISTING client matching the incoming * request to establish a new client identifier at the server */ @@ -8063,7 +8158,7 @@ rfs4_op_setclientid_confirm(nfs_argop4 *argop, nfs_resop4 *resop, * Record clientid in stable storage. * Must be done after server instance has been assigned. */ - rfs4_ss_clid(cp, req); + rfs4_ss_clid(cp); rfs4_dbe_unlock(cp->rc_dbe); @@ -9363,3 +9458,283 @@ rdma_setup_read_data4(READ4args *args, READ4res *rok) rok->wlist = wcl; return (TRUE); } + +/* tunable to disable server referrals */ +int rfs4_no_referrals = 0; + +/* + * Find an NFS record in reparse point data. + * Returns 0 for success and <0 or an errno value on failure. + */ +int +vn_find_nfs_record(vnode_t *vp, nvlist_t **nvlp, char **svcp, char **datap) +{ + int err; + char *stype, *val; + nvlist_t *nvl; + nvpair_t *curr; + + if ((nvl = reparse_init()) == NULL) + return (-1); + + if ((err = reparse_vnode_parse(vp, nvl)) != 0) { + reparse_free(nvl); + return (err); + } + + curr = NULL; + while ((curr = nvlist_next_nvpair(nvl, curr)) != NULL) { + if ((stype = nvpair_name(curr)) == NULL) { + reparse_free(nvl); + return (-2); + } + if (strncasecmp(stype, "NFS", 3) == 0) + break; + } + + if ((curr == NULL) || + (nvpair_value_string(curr, &val))) { + reparse_free(nvl); + return (-3); + } + *nvlp = nvl; + *svcp = stype; + *datap = val; + return (0); +} + +int +vn_is_nfs_reparse(vnode_t *vp, cred_t *cr) +{ + nvlist_t *nvl; + char *s, *d; + + if (rfs4_no_referrals != 0) + return (B_FALSE); + + if (vn_is_reparse(vp, cr, NULL) == B_FALSE) + return (B_FALSE); + + if (vn_find_nfs_record(vp, &nvl, &s, &d) != 0) + return (B_FALSE); + + reparse_free(nvl); + + return (B_TRUE); +} + +/* + * There is a user-level copy of this routine in ref_subr.c. + * Changes should be kept in sync. + */ +static int +nfs4_create_components(char *path, component4 *comp4) +{ + int slen, plen, ncomp; + char *ori_path, *nxtc, buf[MAXNAMELEN]; + + if (path == NULL) + return (0); + + plen = strlen(path) + 1; /* include the terminator */ + ori_path = path; + ncomp = 0; + + /* count number of components in the path */ + for (nxtc = path; nxtc < ori_path + plen; nxtc++) { + if (*nxtc == '/' || *nxtc == '\0' || *nxtc == '\n') { + if ((slen = nxtc - path) == 0) { + path = nxtc + 1; + continue; + } + + if (comp4 != NULL) { + bcopy(path, buf, slen); + buf[slen] = '\0'; + str_to_utf8(buf, &comp4[ncomp]); + } + + ncomp++; /* 1 valid component */ + path = nxtc + 1; + } + if (*nxtc == '\0' || *nxtc == '\n') + break; + } + + return (ncomp); +} + +/* + * There is a user-level copy of this routine in ref_subr.c. + * Changes should be kept in sync. + */ +static int +make_pathname4(char *path, pathname4 *pathname) +{ + int ncomp; + component4 *comp4; + + if (pathname == NULL) + return (0); + + if (path == NULL) { + pathname->pathname4_val = NULL; + pathname->pathname4_len = 0; + return (0); + } + + /* count number of components to alloc buffer */ + if ((ncomp = nfs4_create_components(path, NULL)) == 0) { + pathname->pathname4_val = NULL; + pathname->pathname4_len = 0; + return (0); + } + comp4 = kmem_zalloc(ncomp * sizeof (component4), KM_SLEEP); + + /* copy components into allocated buffer */ + ncomp = nfs4_create_components(path, comp4); + + pathname->pathname4_val = comp4; + pathname->pathname4_len = ncomp; + + return (ncomp); +} + +#define xdr_fs_locations4 xdr_fattr4_fs_locations + +fs_locations4 * +fetch_referral(vnode_t *vp, cred_t *cr) +{ + nvlist_t *nvl; + char *stype, *sdata; + fs_locations4 *result; + char buf[1024]; + size_t bufsize; + XDR xdr; + int err; + + /* + * Check attrs to ensure it's a reparse point + */ + if (vn_is_reparse(vp, cr, NULL) == B_FALSE) + return (NULL); + + /* + * Look for an NFS record and get the type and data + */ + if (vn_find_nfs_record(vp, &nvl, &stype, &sdata) != 0) + return (NULL); + + /* + * With the type and data, upcall to get the referral + */ + bufsize = sizeof (buf); + bzero(buf, sizeof (buf)); + err = reparse_kderef((const char *)stype, (const char *)sdata, + buf, &bufsize); + reparse_free(nvl); + + DTRACE_PROBE4(nfs4serv__func__referral__upcall, + char *, stype, char *, sdata, char *, buf, int, err); + if (err) { + cmn_err(CE_NOTE, + "reparsed daemon not running: unable to get referral (%d)", + err); + return (NULL); + } + + /* + * We get an XDR'ed record back from the kderef call + */ + xdrmem_create(&xdr, buf, bufsize, XDR_DECODE); + result = kmem_alloc(sizeof (fs_locations4), KM_SLEEP); + err = xdr_fs_locations4(&xdr, result); + XDR_DESTROY(&xdr); + if (err != TRUE) { + DTRACE_PROBE1(nfs4serv__func__referral__upcall__xdrfail, + int, err); + return (NULL); + } + + /* + * Look at path to recover fs_root, ignoring the leading '/' + */ + (void) make_pathname4(vp->v_path, &result->fs_root); + + return (result); +} + +char * +build_symlink(vnode_t *vp, cred_t *cr, size_t *strsz) +{ + fs_locations4 *fsl; + fs_location4 *fs; + char *server, *path, *symbuf; + static char *prefix = "/net/"; + int i, size, npaths; + uint_t len; + + /* Get the referral */ + if ((fsl = fetch_referral(vp, cr)) == NULL) + return (NULL); + + /* Deal with only the first location and first server */ + fs = &fsl->locations_val[0]; + server = utf8_to_str(&fs->server_val[0], &len, NULL); + if (server == NULL) { + rfs4_free_fs_locations4(fsl); + kmem_free(fsl, sizeof (fs_locations4)); + return (NULL); + } + + /* Figure out size for "/net/" + host + /path/path/path + NULL */ + size = strlen(prefix) + len; + for (i = 0; i < fs->rootpath.pathname4_len; i++) + size += fs->rootpath.pathname4_val[i].utf8string_len + 1; + + /* Allocate the symlink buffer and fill it */ + symbuf = kmem_zalloc(size, KM_SLEEP); + (void) strcat(symbuf, prefix); + (void) strcat(symbuf, server); + kmem_free(server, len); + + npaths = 0; + for (i = 0; i < fs->rootpath.pathname4_len; i++) { + path = utf8_to_str(&fs->rootpath.pathname4_val[i], &len, NULL); + if (path == NULL) + continue; + (void) strcat(symbuf, "/"); + (void) strcat(symbuf, path); + npaths++; + kmem_free(path, len); + } + + rfs4_free_fs_locations4(fsl); + kmem_free(fsl, sizeof (fs_locations4)); + + if (strsz != NULL) + *strsz = size; + return (symbuf); +} + +/* + * Check to see if we have a downrev Solaris client, so that we + * can send it a symlink instead of a referral. + */ +int +client_is_downrev(struct svc_req *req) +{ + struct sockaddr *ca; + rfs4_clntip_t *ci; + bool_t create = FALSE; + int is_downrev; + + ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf; + ASSERT(ca); + ci = rfs4_find_clntip(ca, &create); + if (ci == NULL) + return (0); + is_downrev = ci->ri_no_referrals; + rfs4_dbe_rele(ci->ri_dbe); + return (is_downrev); +} diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c b/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c index 729718b5b9..c1f64d69bc 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c +++ b/usr/src/uts/common/fs/nfs/nfs4_srv_attr.c @@ -29,6 +29,9 @@ #include <nfs/export.h> #include <nfs/nfs4.h> #include <sys/ddi.h> +#include <sys/door.h> +#include <sys/sdt.h> +#include <nfs/nfssys.h> void rfs4_init_compound_state(struct compound_state *); @@ -141,6 +144,7 @@ rfs4_attr_init() sarg.flag = 0; sarg.rdattr_error = NFS4_OK; sarg.rdattr_error_req = FALSE; + sarg.is_referral = B_FALSE; rfs4_ntov_init(); @@ -634,7 +638,10 @@ rfs4_fattr4_fsid(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, error = EINVAL; break; /* this attr is supported */ case NFS4ATTR_GETIT: - if (sarg->cs->exi->exi_volatile_dev) { + if (sarg->is_referral) { + na->fsid.major = 1; + na->fsid.minor = 0; + } else if (sarg->cs->exi->exi_volatile_dev) { pmaj[0] = sarg->cs->exi->exi_fsid.val[0]; pmaj[1] = sarg->cs->exi->exi_fsid.val[1]; na->fsid.minor = 0; @@ -647,7 +654,11 @@ rfs4_fattr4_fsid(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, error = EINVAL; break; case NFS4ATTR_VERIT: - if (sarg->cs->exi->exi_volatile_dev) { + if (sarg->is_referral) { + if (na->fsid.major != 1 || + na->fsid.minor != 0) + error = -1; + } else if (sarg->cs->exi->exi_volatile_dev) { if (pmaj[0] != sarg->cs->exi->exi_fsid.val[0] || pmaj[1] != sarg->cs->exi->exi_fsid.val[1] || na->fsid.minor != 0) @@ -1495,12 +1506,109 @@ rfs4_fattr4_files_total(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, return (error); } +static void +rfs4_free_pathname4(pathname4 *pn4) +{ + int i, len; + utf8string *utf8s; + + if (pn4 == NULL || (len = pn4->pathname4_len) == 0 || + (utf8s = pn4->pathname4_val) == NULL) + return; + + for (i = 0; i < len; i++, utf8s++) { + if (utf8s->utf8string_val == NULL || + utf8s->utf8string_len == 0) + continue; + + kmem_free(utf8s->utf8string_val, utf8s->utf8string_len); + utf8s->utf8string_val = NULL; + } + + kmem_free(pn4->pathname4_val, + sizeof (utf8string) * pn4->pathname4_len); + pn4->pathname4_val = 0; +} + +static void +rfs4_free_fs_location4(fs_location4 *fsl4) +{ + if (fsl4 == NULL) + return; + + rfs4_free_pathname4((pathname4 *)&fsl4->server_len); + rfs4_free_pathname4(&fsl4->rootpath); +} + +void +rfs4_free_fs_locations4(fs_locations4 *fsls4) +{ + int i, len; + fs_location4 *fsl4; + + if (fsls4 == NULL) + return; + + /* free fs_root */ + rfs4_free_pathname4(&fsls4->fs_root); + + if ((len = fsls4->locations_len) == 0 || + (fsl4 = fsls4->locations_val) == NULL) + return; + + /* free fs_location4 */ + for (i = 0; i < len; i++) { + rfs4_free_fs_location4(fsl4); + fsl4++; + } + + kmem_free(fsls4->locations_val, sizeof (fs_location4) * len); + fsls4->locations_val = NULL; +} + /* ARGSUSED */ static int rfs4_fattr4_fs_locations(nfs4_attr_cmd_t cmd, struct nfs4_svgetit_arg *sarg, union nfs4_attr_u *na) { - return (ENOTSUP); + int error = 0; + fs_locations4 *fsl; + + if (RFS4_MANDATTR_ONLY) + return (ENOTSUP); + + switch (cmd) { + case NFS4ATTR_SUPPORTED: + if (sarg->op == NFS4ATTR_SETIT || sarg->op == NFS4ATTR_VERIT) + error = EINVAL; + break; /* this attr is supported */ + + case NFS4ATTR_GETIT: + fsl = fetch_referral(sarg->cs->vp, sarg->cs->cr); + if (fsl == NULL) + error = EINVAL; + else { + na->fs_locations = *fsl; + kmem_free(fsl, sizeof (fs_locations4)); + } + global_svstat_ptr[4][NFS_REFERRALS].value.ui64++; + break; + + case NFS4ATTR_FREEIT: + if (sarg->op == NFS4ATTR_SETIT || sarg->op == NFS4ATTR_VERIT) + error = EINVAL; + rfs4_free_fs_locations4(&na->fs_locations); + break; + + case NFS4ATTR_SETIT: + case NFS4ATTR_VERIT: + /* + * read-only attr + */ + error = EINVAL; + break; + } + return (error); } /* ARGSUSED */ diff --git a/usr/src/uts/common/fs/nfs/nfs4_srv_readdir.c b/usr/src/uts/common/fs/nfs/nfs4_srv_readdir.c index debdacc1cd..3069a98835 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_srv_readdir.c +++ b/usr/src/uts/common/fs/nfs/nfs4_srv_readdir.c @@ -97,7 +97,6 @@ static nfs_ftype4 vt_to_nf4[] = { 0, NF4REG, NF4DIR, NF4BLK, NF4CHR, NF4LNK, NF4FIFO, 0, 0, NF4SOCK, 0 }; - int nfs4_readdir_getvp(vnode_t *dvp, char *d_name, vnode_t **vpp, struct exportinfo **exi, struct svc_req *req, @@ -117,8 +116,20 @@ nfs4_readdir_getvp(vnode_t *dvp, char *d_name, vnode_t **vpp, NULL, NULL, NULL)) return (error); + /* + * If the directory is a referral point, don't return the + * attrs, instead set rdattr_error to MOVED. + */ + if (vn_is_nfs_reparse(vp, cs->cr) && !client_is_downrev(req)) { + VN_RELE(vp); + DTRACE_PROBE2(nfs4serv__func__referral__moved, + vnode_t *, vp, char *, "nfs4_readdir_getvp"); + return (NFS4ERR_MOVED); + } + /* Is this object mounted upon? */ ismntpt = vn_ismntpt(vp); + /* * Nothing more to do if object is not a mount point or * a possible LOFS shadow of an LOFS mount (which won't @@ -141,6 +152,13 @@ nfs4_readdir_getvp(vnode_t *dvp, char *d_name, vnode_t **vpp, VN_RELE(pre_tvp); return (error); } + if (vn_is_nfs_reparse(vp, cs->cr)) { + VN_RELE(vp); + VN_RELE(pre_tvp); + DTRACE_PROBE2(nfs4serv__func__referral__moved, + vnode_t *, vp, char *, "nfs4_readdir_getvp"); + return (NFS4ERR_MOVED); + } } bzero(&fid, sizeof (fid)); @@ -818,9 +836,18 @@ reencode_attrs: va.va_mask = AT_ALL; rddirattr_error = VOP_GETATTR(vp, &va, 0, cs->cr, NULL); - if (rddirattr_error) + if (rddirattr_error) { ae = ar & (FATTR4_RDATTR_ERROR_MASK | FATTR4_MOUNTED_ON_FILEID_MASK); + } else { + /* + * We may lie about the object + * type for a referral + */ + if (vn_is_nfs_reparse(vp, cs->cr) && + client_is_downrev(req)) + va.va_type = VLNK; + } } } diff --git a/usr/src/uts/common/fs/nfs/nfs4_state.c b/usr/src/uts/common/fs/nfs/nfs4_state.c index ef0f2c800b..0659e8c253 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_state.c +++ b/usr/src/uts/common/fs/nfs/nfs4_state.c @@ -288,6 +288,8 @@ rfs4_table_t *rfs4_client_tab; static rfs4_index_t *rfs4_clientid_idx; static rfs4_index_t *rfs4_nfsclnt_idx; +static rfs4_table_t *rfs4_clntip_tab; +static rfs4_index_t *rfs4_clntip_idx; static rfs4_table_t *rfs4_openowner_tab; static rfs4_index_t *rfs4_openowner_idx; static rfs4_table_t *rfs4_state_tab; @@ -330,6 +332,7 @@ static rfs4_index_t *rfs4_deleg_state_idx; static time_t rfs4_client_cache_time = 0; +static time_t rfs4_clntip_cache_time = 0; static time_t rfs4_openowner_cache_time = 0; static time_t rfs4_state_cache_time = 0; static time_t rfs4_lo_state_cache_time = 0; @@ -348,6 +351,12 @@ static void *clientid_mkkey(rfs4_entry_t); static uint32_t nfsclnt_hash(void *); static bool_t nfsclnt_compare(rfs4_entry_t, void *); static void *nfsclnt_mkkey(rfs4_entry_t); +static bool_t rfs4_clntip_expiry(rfs4_entry_t); +static void rfs4_clntip_destroy(rfs4_entry_t); +static bool_t rfs4_clntip_create(rfs4_entry_t, void *); +static uint32_t clntip_hash(void *); +static bool_t clntip_compare(rfs4_entry_t, void *); +static void *clntip_mkkey(rfs4_entry_t); static bool_t rfs4_openowner_create(rfs4_entry_t, void *); static void rfs4_openowner_destroy(rfs4_entry_t); static bool_t rfs4_openowner_expiry(rfs4_entry_t); @@ -883,7 +892,7 @@ rfs4_ss_chkclid_sip(rfs4_client_t *cp, rfs4_servinst_t *sip) * the server-generated short-hand clientid. */ void -rfs4_ss_clid(rfs4_client_t *cp, struct svc_req *req) +rfs4_ss_clid(rfs4_client_t *cp) { const char *kinet_ntop6(uchar_t *, char *, size_t); char leaf[MAXNAMELEN], buf[INET6_ADDRSTRLEN]; @@ -896,19 +905,12 @@ rfs4_ss_clid(rfs4_client_t *cp, struct svc_req *req) buf[0] = 0; - - ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf; - if (ca == NULL) { - return; - } + ca = (struct sockaddr *)&cp->rc_addr; /* * Convert the caller's IP address to a dotted string */ if (ca->sa_family == AF_INET) { - - bcopy(svc_getrpccaller(req->rq_xprt)->buf, &cp->rc_addr, - sizeof (struct sockaddr_in)); b = (uchar_t *)&((struct sockaddr_in *)ca)->sin_addr; (void) sprintf(buf, "%03d.%03d.%03d.%03d", b[0] & 0xFF, b[1] & 0xFF, b[2] & 0xFF, b[3] & 0xFF); @@ -916,8 +918,6 @@ rfs4_ss_clid(rfs4_client_t *cp, struct svc_req *req) struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)ca; - bcopy(svc_getrpccaller(req->rq_xprt)->buf, &cp->rc_addr, - sizeof (struct sockaddr_in6)); (void) kinet_ntop6((uchar_t *)&sin6->sin6_addr, buf, INET6_ADDRSTRLEN); } @@ -1257,6 +1257,22 @@ rfs4_state_init() clientid_compare, clientid_mkkey, FALSE); + rfs4_clntip_cache_time = 86400 * 365; /* about a year */ + rfs4_clntip_tab = rfs4_table_create(rfs4_server_state, + "ClntIP", + rfs4_clntip_cache_time, + 1, + rfs4_clntip_create, + rfs4_clntip_destroy, + rfs4_clntip_expiry, + sizeof (rfs4_clntip_t), + TABSIZE, + MAXTABSZ, 100); + rfs4_clntip_idx = rfs4_index_create(rfs4_clntip_tab, + "client_ip", clntip_hash, + clntip_compare, clntip_mkkey, + TRUE); + rfs4_openowner_cache_time *= rfs4_lease_time; rfs4_openowner_tab = rfs4_table_create(rfs4_server_state, "OpenOwner", @@ -1649,6 +1665,7 @@ rfs4_client_create(rfs4_entry_t u_entry, void *arg) { rfs4_client_t *cp = (rfs4_client_t *)u_entry; nfs_client_id4 *client = (nfs_client_id4 *)arg; + struct sockaddr *ca; cid *cidp; scid_confirm_verf *scvp; @@ -1667,6 +1684,14 @@ rfs4_client_create(rfs4_entry_t u_entry, void *arg) bcopy(client->id_val, cp->rc_nfs_client.id_val, client->id_len); cp->rc_nfs_client.verifier = client->verifier; + /* Copy client's IP address */ + ca = client->cl_addr; + if (ca->sa_family == AF_INET) + bcopy(ca, &cp->rc_addr, sizeof (struct sockaddr_in)); + else if (ca->sa_family == AF_INET6) + bcopy(ca, &cp->rc_addr, sizeof (struct sockaddr_in6)); + cp->rc_nfs_client.cl_addr = (struct sockaddr *)&cp->rc_addr; + /* Init the value for the SETCLIENTID_CONFIRM verifier */ scvp = (scid_confirm_verf *)&cp->rc_confirm_verf; scvp->cv_impl.c_id = cidp->impl_id.c_id; @@ -1781,6 +1806,119 @@ rfs4_findclient_by_id(clientid4 clientid, bool_t find_unconfirmed) } } +static uint32_t +clntip_hash(void *key) +{ + struct sockaddr *addr = key; + int i, len = 0; + uint32_t hash = 0; + + if (addr->sa_family == AF_INET) + len = sizeof (struct sockaddr_in); + else if (addr->sa_family == AF_INET6) + len = sizeof (struct sockaddr_in6); + + for (i = 0; i < len; i++) { + hash <<= 1; + hash += (uint_t)(((char *)addr)[i]); + } + return (hash); +} + +static bool_t +clntip_compare(rfs4_entry_t entry, void *key) +{ + rfs4_clntip_t *cp = (rfs4_clntip_t *)entry; + struct sockaddr *addr = key; + int len = 0; + + if (addr->sa_family == AF_INET) + len = sizeof (struct sockaddr_in); + else if (addr->sa_family == AF_INET6) + len = sizeof (struct sockaddr_in6); + else + return (0); + + return (bcmp(&cp->ri_addr, addr, len) == 0); +} + +static void * +clntip_mkkey(rfs4_entry_t entry) +{ + rfs4_clntip_t *cp = (rfs4_clntip_t *)entry; + + return (&cp->ri_addr); +} + +static bool_t +rfs4_clntip_expiry(rfs4_entry_t u_entry) +{ + rfs4_clntip_t *cp = (rfs4_clntip_t *)u_entry; + + if (rfs4_dbe_is_invalid(cp->ri_dbe)) + return (TRUE); + return (FALSE); +} + +/* ARGSUSED */ +static void +rfs4_clntip_destroy(rfs4_entry_t u_entry) +{ +} + +static bool_t +rfs4_clntip_create(rfs4_entry_t u_entry, void *arg) +{ + rfs4_clntip_t *cp = (rfs4_clntip_t *)u_entry; + struct sockaddr *ca = (struct sockaddr *)arg; + + /* Copy client's IP address */ + if (ca->sa_family == AF_INET) + bcopy(ca, &cp->ri_addr, sizeof (struct sockaddr_in)); + else if (ca->sa_family == AF_INET6) + bcopy(ca, &cp->ri_addr, sizeof (struct sockaddr_in6)); + else + return (FALSE); + cp->ri_no_referrals = 1; + + return (TRUE); +} + +rfs4_clntip_t * +rfs4_find_clntip(struct sockaddr *addr, bool_t *create) +{ + rfs4_clntip_t *cp; + + rw_enter(&rfs4_findclient_lock, RW_READER); + + cp = (rfs4_clntip_t *)rfs4_dbsearch(rfs4_clntip_idx, addr, + create, addr, RFS4_DBS_VALID); + + rw_exit(&rfs4_findclient_lock); + + return (cp); +} + +void +rfs4_invalidate_clntip(struct sockaddr *addr) +{ + rfs4_clntip_t *cp; + bool_t create = FALSE; + + rw_enter(&rfs4_findclient_lock, RW_READER); + + cp = (rfs4_clntip_t *)rfs4_dbsearch(rfs4_clntip_idx, addr, + &create, NULL, RFS4_DBS_VALID); + if (cp == NULL) { + rw_exit(&rfs4_findclient_lock); + return; + } + rfs4_dbe_invalidate(cp->ri_dbe); + rfs4_dbe_rele(cp->ri_dbe); + + rw_exit(&rfs4_findclient_lock); +} + bool_t rfs4_lease_expired(rfs4_client_t *cp) { diff --git a/usr/src/uts/common/fs/nfs/nfs4_stub_vnops.c b/usr/src/uts/common/fs/nfs/nfs4_stub_vnops.c index 37bc502b0b..40d9236765 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_stub_vnops.c +++ b/usr/src/uts/common/fs/nfs/nfs4_stub_vnops.c @@ -65,6 +65,7 @@ #include <sys/list.h> #include <sys/stat.h> #include <sys/mntent.h> +#include <sys/priv.h> #include <rpc/types.h> #include <rpc/auth.h> @@ -78,6 +79,8 @@ #include <nfs/nfs4_kprot.h> #include <nfs/rnode4.h> #include <nfs/nfs4_clnt.h> +#include <nfs/nfsid_map.h> +#include <nfs/nfs4_idmap_impl.h> #include <vm/hat.h> #include <vm/as.h> @@ -97,6 +100,9 @@ #include <sys/priv_names.h> +extern zone_key_t nfs4clnt_zone_key; +extern zone_key_t nfsidmap_zone_key; + /* * The automatic unmounter thread stuff! */ @@ -202,13 +208,16 @@ extern int nfs4_realvp(vnode_t *, vnode_t **, caller_context_t *); static int nfs4_trigger_mount(vnode_t *, cred_t *, vnode_t **); static int nfs4_trigger_domount(vnode_t *, domount_args_t *, vfs_t **, cred_t *, vnode_t **); -static domount_args_t *nfs4_trigger_domount_args_create(vnode_t *); +static domount_args_t *nfs4_trigger_domount_args_create(vnode_t *, cred_t *); static void nfs4_trigger_domount_args_destroy(domount_args_t *dma, vnode_t *vp); -static ephemeral_servinfo_t *nfs4_trigger_esi_create(vnode_t *, servinfo4_t *); +static ephemeral_servinfo_t *nfs4_trigger_esi_create(vnode_t *, servinfo4_t *, + cred_t *); static void nfs4_trigger_esi_destroy(ephemeral_servinfo_t *, vnode_t *); static ephemeral_servinfo_t *nfs4_trigger_esi_create_mirrormount(vnode_t *, servinfo4_t *); +static ephemeral_servinfo_t *nfs4_trigger_esi_create_referral(vnode_t *, + cred_t *); static struct nfs_args *nfs4_trigger_nargs_create(mntinfo4_t *, servinfo4_t *, ephemeral_servinfo_t *); static void nfs4_trigger_nargs_destroy(struct nfs_args *); @@ -216,10 +225,11 @@ static char *nfs4_trigger_create_mntopts(vfs_t *); static void nfs4_trigger_destroy_mntopts(char *); static int nfs4_trigger_add_mntopt(char *, char *, vfs_t *); static enum clnt_stat nfs4_trigger_ping_server(servinfo4_t *, int); +static enum clnt_stat nfs4_ping_server_common(struct knetconfig *, + struct netbuf *, int); extern int umount2_engine(vfs_t *, int, cred_t *, int); - vnodeops_t *nfs4_trigger_vnodeops; /* @@ -372,12 +382,46 @@ nfs4_trigger_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) return (VOP_OPEN(vpp, flag, cr, ct)); } +void +nfs4_fake_attrs(vnode_t *vp, struct vattr *vap) +{ + uint_t mask; + timespec_t now; + + /* + * Set some attributes here for referrals. + */ + mask = vap->va_mask; + bzero(vap, sizeof (struct vattr)); + vap->va_mask = mask; + vap->va_uid = 0; + vap->va_gid = 0; + vap->va_nlink = 1; + vap->va_size = 1; + gethrestime(&now); + vap->va_atime = now; + vap->va_mtime = now; + vap->va_ctime = now; + vap->va_type = VDIR; + vap->va_mode = 0555; + vap->va_fsid = vp->v_vfsp->vfs_dev; + vap->va_rdev = 0; + vap->va_blksize = MAXBSIZE; + vap->va_nblocks = 1; + vap->va_seq = 0; +} + /* * For the majority of cases, nfs4_trigger_getattr() will not trigger * a mount. However, if ATTR_TRIGGER is set, we are being informed * that we need to force the mount before we attempt to determine * the attributes. The intent is an atomic operation for security * testing. + * + * If we're not triggering a mount, we can still inquire about the + * actual attributes from the server in the mirror mount case, + * and will return manufactured attributes for a referral (see + * the 'create' branch of find_referral_stubvp()). */ static int nfs4_trigger_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr, @@ -394,8 +438,15 @@ nfs4_trigger_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr, error = VOP_GETATTR(newvp, vap, flags, cr, ct); VN_RELE(newvp); - } else { + + } else if (RP_ISSTUB_MIRRORMOUNT(VTOR4(vp))) { + error = nfs4_getattr(vp, vap, flags, cr, ct); + + } else if (RP_ISSTUB_REFERRAL(VTOR4(vp))) { + + nfs4_fake_attrs(vp, vap); + error = 0; } return (error); @@ -446,17 +497,19 @@ nfs4_trigger_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, ASSERT(RP_ISSTUB(drp)); - /* for now, we only support mirror-mounts */ - ASSERT(RP_ISSTUB_MIRRORMOUNT(drp)); - /* * It's not legal to lookup ".." for an fs root, so we mustn't pass * that up. Instead, pass onto the regular op, regardless of whether * we've triggered a mount. */ if (strcmp(nm, "..") == 0) - return (nfs4_lookup(dvp, nm, vpp, pnp, flags, rdir, cr, - ct, deflags, rpnp)); + if (RP_ISSTUB_MIRRORMOUNT(drp)) { + return (nfs4_lookup(dvp, nm, vpp, pnp, flags, rdir, cr, + ct, deflags, rpnp)); + } else if (RP_ISSTUB_REFERRAL(drp)) { + /* Return the parent vnode */ + return (vtodv(dvp, vpp, cr, TRUE)); + } error = nfs4_trigger_mount(dvp, cr, &newdvp); if (error) @@ -672,7 +725,7 @@ nfs4_trigger_mounted_already(vnode_t *vp, vnode_t **newvpp, } /* - * Mount upon a trigger vnode; for mirror-mounts, etc. + * Mount upon a trigger vnode; for mirror-mounts, referrals, etc. * * The mount may have already occurred, via another thread. If not, * assemble the location information - which may require fetching - and @@ -706,9 +759,6 @@ nfs4_trigger_mount(vnode_t *vp, cred_t *cr, vnode_t **newvpp) ASSERT(RP_ISSTUB(rp)); - /* for now, we only support mirror-mounts */ - ASSERT(RP_ISSTUB_MIRRORMOUNT(rp)); - *newvpp = NULL; /* @@ -782,7 +832,7 @@ nfs4_trigger_mount(vnode_t *vp, cred_t *cr, vnode_t **newvpp) must_unlock = TRUE; - dma = nfs4_trigger_domount_args_create(vp); + dma = nfs4_trigger_domount_args_create(vp, cr); if (dma == NULL) { error = EINVAL; goto done; @@ -801,10 +851,15 @@ nfs4_trigger_mount(vnode_t *vp, cred_t *cr, vnode_t **newvpp) } crset_zone_privall(mcred); + if (is_system_labeled()) + (void) setpflags(NET_MAC_AWARE, 1, mcred); error = nfs4_trigger_domount(vp, dma, &vfsp, mcred, newvpp); nfs4_trigger_domount_args_destroy(dma, vp); + DTRACE_PROBE2(nfs4clnt__func__referral__mount, + vnode_t *, vp, int, error); + crfree(mcred); done: @@ -812,9 +867,20 @@ done: if (must_unlock) { mutex_enter(&net->net_cnt_lock); net->net_status &= ~NFS4_EPHEMERAL_TREE_MOUNTING; + + /* + * REFCNT: If we are the root of the tree, then we need + * to keep a reference because we malloced the tree and + * this is where we tied it to our mntinfo. + * + * If we are not the root of the tree, then our tie to + * the mntinfo occured elsewhere and we need to + * decrement the reference to the tree. + */ if (is_building) net->net_status &= ~NFS4_EPHEMERAL_TREE_BUILDING; - nfs4_ephemeral_tree_decr(net); + else + nfs4_ephemeral_tree_decr(net); mutex_exit(&net->net_cnt_lock); mutex_exit(&net->net_tree_lock); @@ -830,7 +896,7 @@ done: * Collect together both the generic & mount-type specific args. */ static domount_args_t * -nfs4_trigger_domount_args_create(vnode_t *vp) +nfs4_trigger_domount_args_create(vnode_t *vp, cred_t *cr) { int nointr; char *hostlist; @@ -848,7 +914,7 @@ nfs4_trigger_domount_args_create(vnode_t *vp) /* check if the current server is responding */ status = nfs4_trigger_ping_server(svp, nointr); if (status == RPC_SUCCESS) { - esi_first = nfs4_trigger_esi_create(vp, svp); + esi_first = nfs4_trigger_esi_create(vp, svp, cr); if (esi_first == NULL) { kmem_free(hostlist, MAXPATHLEN); return (NULL); @@ -924,7 +990,7 @@ nfs4_trigger_domount_args_create(vnode_t *vp) if (status != RPC_SUCCESS) continue; - esi = nfs4_trigger_esi_create(vp, svp); + esi = nfs4_trigger_esi_create(vp, svp, cr); if (esi == NULL) continue; @@ -1006,7 +1072,7 @@ nfs4_trigger_domount_args_destroy(domount_args_t *dma, vnode_t *vp) * types of ephemeral mount, the way we gather its contents differs. */ static ephemeral_servinfo_t * -nfs4_trigger_esi_create(vnode_t *vp, servinfo4_t *svp) +nfs4_trigger_esi_create(vnode_t *vp, servinfo4_t *svp, cred_t *cr) { ephemeral_servinfo_t *esi; rnode4_t *rp = VTOR4(vp); @@ -1016,12 +1082,10 @@ nfs4_trigger_esi_create(vnode_t *vp, servinfo4_t *svp) /* Call the ephemeral type-specific routine */ if (RP_ISSTUB_MIRRORMOUNT(rp)) esi = nfs4_trigger_esi_create_mirrormount(vp, svp); + else if (RP_ISSTUB_REFERRAL(rp)) + esi = nfs4_trigger_esi_create_referral(vp, cr); else esi = NULL; - - /* for now, we only support mirror-mounts */ - ASSERT(esi != NULL); - return (esi); } @@ -1032,9 +1096,6 @@ nfs4_trigger_esi_destroy(ephemeral_servinfo_t *esi, vnode_t *vp) ASSERT(RP_ISSTUB(rp)); - /* for now, we only support mirror-mounts */ - ASSERT(RP_ISSTUB_MIRRORMOUNT(rp)); - /* Currently, no need for an ephemeral type-specific routine */ /* @@ -1051,6 +1112,10 @@ nfs4_trigger_esi_destroy(ephemeral_servinfo_t *esi, vnode_t *vp) * in which case it should be moved to nfs4_trigger_esi_create(), or a * common function called. */ + +/* + * Mirror mounts case - should have all data available + */ static ephemeral_servinfo_t * nfs4_trigger_esi_create_mirrormount(vnode_t *vp, servinfo4_t *svp) { @@ -1149,9 +1214,12 @@ nfs4_trigger_esi_create_mirrormount(vnode_t *vp, servinfo4_t *svp) stubpath += 1; /* for nfs_args->fh */ - esi->esi_path_len = strlen(svp->sv_path) + strlen(stubpath) + 1; + esi->esi_path_len = strlen(stubpath) + 1; + if (strcmp(svp->sv_path, "/") != 0) + esi->esi_path_len += strlen(svp->sv_path); esi->esi_path = kmem_zalloc(esi->esi_path_len, KM_SLEEP); - (void) strcat(esi->esi_path, svp->sv_path); + if (strcmp(svp->sv_path, "/") != 0) + (void) strcat(esi->esi_path, svp->sv_path); (void) strcat(esi->esi_path, stubpath); stubpath -= 1; @@ -1164,6 +1232,592 @@ nfs4_trigger_esi_create_mirrormount(vnode_t *vp, servinfo4_t *svp) } /* + * Makes an upcall to NFSMAPID daemon to resolve hostname of NFS server to + * get network information required to do the mount call. + */ +int +nfs4_callmapid(utf8string *server, struct nfs_fsl_info *resp) +{ + door_arg_t door_args; + door_handle_t dh; + XDR xdr; + refd_door_args_t *xdr_argsp; + refd_door_res_t *orig_resp; + k_sigset_t smask; + int xdr_len = 0; + int res_len = 16; /* length of an ip adress */ + int orig_reslen = res_len; + int error = 0; + struct nfsidmap_globals *nig; + + if (zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN) + return (ECONNREFUSED); + + nig = zone_getspecific(nfsidmap_zone_key, nfs_zone()); + ASSERT(nig != NULL); + + mutex_enter(&nig->nfsidmap_daemon_lock); + dh = nig->nfsidmap_daemon_dh; + if (dh == NULL) { + mutex_exit(&nig->nfsidmap_daemon_lock); + cmn_err(CE_NOTE, + "nfs4_callmapid: nfsmapid daemon not " \ + "running unable to resolve host name\n"); + return (EINVAL); + } + door_ki_hold(dh); + mutex_exit(&nig->nfsidmap_daemon_lock); + + xdr_len = xdr_sizeof(&(xdr_utf8string), server); + + xdr_argsp = kmem_zalloc(xdr_len + sizeof (*xdr_argsp), KM_SLEEP); + xdr_argsp->xdr_len = xdr_len; + xdr_argsp->cmd = NFSMAPID_SRV_NETINFO; + + xdrmem_create(&xdr, (char *)&xdr_argsp->xdr_arg, + xdr_len, XDR_ENCODE); + + if (!xdr_utf8string(&xdr, server)) { + kmem_free(xdr_argsp, xdr_len + sizeof (*xdr_argsp)); + door_ki_rele(dh); + return (1); + } + + if (orig_reslen) + orig_resp = kmem_alloc(orig_reslen, KM_SLEEP); + + door_args.data_ptr = (char *)xdr_argsp; + door_args.data_size = sizeof (*xdr_argsp) + xdr_argsp->xdr_len; + door_args.desc_ptr = NULL; + door_args.desc_num = 0; + door_args.rbuf = orig_resp ? (char *)orig_resp : NULL; + door_args.rsize = res_len; + + sigintr(&smask, 1); + error = door_ki_upcall(dh, &door_args); + sigunintr(&smask); + + door_ki_rele(dh); + + kmem_free(xdr_argsp, xdr_len + sizeof (*xdr_argsp)); + if (error) { + kmem_free(orig_resp, orig_reslen); + /* + * There is no door to connect to. The referral daemon + * must not be running yet. + */ + cmn_err(CE_WARN, + "nfsmapid not running cannot resolve host name"); + goto out; + } + + /* + * If the results buffer passed back are not the same as + * what was sent free the old buffer and use the new one. + */ + if (orig_resp && orig_reslen) { + refd_door_res_t *door_resp; + + door_resp = (refd_door_res_t *)door_args.rbuf; + if ((void *)door_args.rbuf != orig_resp) + kmem_free(orig_resp, orig_reslen); + if (door_resp->res_status == 0) { + xdrmem_create(&xdr, (char *)&door_resp->xdr_res, + door_resp->xdr_len, XDR_DECODE); + bzero(resp, sizeof (struct nfs_fsl_info)); + if (!xdr_nfs_fsl_info(&xdr, resp)) { + DTRACE_PROBE2( + nfs4clnt__debug__referral__upcall__xdrfail, + struct nfs_fsl_info *, resp, + char *, "nfs4_callmapid"); + error = EINVAL; + } + } else { + DTRACE_PROBE2( + nfs4clnt__debug__referral__upcall__badstatus, + int, door_resp->res_status, + char *, "nfs4_callmapid"); + error = door_resp->res_status; + } + kmem_free(door_args.rbuf, door_args.rsize); + } +out: + DTRACE_PROBE2(nfs4clnt__func__referral__upcall, + char *, server, int, error); + return (error); +} + +/* + * Fetches the fs_locations attribute. Typically called + * from a Replication/Migration/Referrals/Mirror-mount context + * + * Fills in the attributes in garp. The caller is assumed + * to have allocated memory for garp. + * + * lock: if set do not lock s_recovlock and mi_recovlock mutex, + * it's already done by caller. Otherwise lock these mutexes + * before doing the rfs4call(). + * + * Returns + * 1 for success + * 0 for failure + */ +int +nfs4_fetch_locations(mntinfo4_t *mi, nfs4_sharedfh_t *sfh, char *nm, + cred_t *cr, nfs4_ga_res_t *garp, COMPOUND4res_clnt *callres, bool_t lock) +{ + COMPOUND4args_clnt args; + COMPOUND4res_clnt res; + nfs_argop4 *argop; + int argoplist_size = 3 * sizeof (nfs_argop4); + nfs4_server_t *sp = NULL; + int doqueue = 1; + nfs4_error_t e = { 0, NFS4_OK, RPC_SUCCESS }; + int retval = 1; + struct nfs4_clnt *nfscl; + + if (lock == TRUE) + (void) nfs_rw_enter_sig(&mi->mi_recovlock, RW_READER, 0); + else + ASSERT(nfs_rw_lock_held(&mi->mi_recovlock, RW_READER) || + nfs_rw_lock_held(&mi->mi_recovlock, RW_WRITER)); + + sp = find_nfs4_server(mi); + if (lock == TRUE) + nfs_rw_exit(&mi->mi_recovlock); + + if (sp != NULL) + mutex_exit(&sp->s_lock); + + if (lock == TRUE) { + if (sp != NULL) + (void) nfs_rw_enter_sig(&sp->s_recovlock, + RW_WRITER, 0); + (void) nfs_rw_enter_sig(&mi->mi_recovlock, RW_WRITER, 0); + } else { + if (sp != NULL) { + ASSERT(nfs_rw_lock_held(&sp->s_recovlock, RW_READER) || + nfs_rw_lock_held(&sp->s_recovlock, RW_WRITER)); + } + } + + /* + * Do we want to do the setup for recovery here? + * + * We know that the server responded to a null ping a very + * short time ago, and we know that we intend to do a + * single stateless operation - we want to fetch attributes, + * so we know we can't encounter errors about state. If + * something goes wrong with the GETATTR, like not being + * able to get a response from the server or getting any + * kind of FH error, we should fail the mount. + * + * We may want to re-visited this at a later time. + */ + argop = kmem_alloc(argoplist_size, KM_SLEEP); + + args.ctag = TAG_GETATTR_FSLOCATION; + /* PUTFH LOOKUP GETATTR */ + args.array_len = 3; + args.array = argop; + + /* 0. putfh file */ + argop[0].argop = OP_CPUTFH; + argop[0].nfs_argop4_u.opcputfh.sfh = sfh; + + /* 1. lookup name, can't be dotdot */ + argop[1].argop = OP_CLOOKUP; + argop[1].nfs_argop4_u.opclookup.cname = nm; + + /* 2. file attrs */ + argop[2].argop = OP_GETATTR; + argop[2].nfs_argop4_u.opgetattr.attr_request = + FATTR4_FSID_MASK | FATTR4_FS_LOCATIONS_MASK | + FATTR4_MOUNTED_ON_FILEID_MASK; + argop[2].nfs_argop4_u.opgetattr.mi = mi; + + rfs4call(mi, &args, &res, cr, &doqueue, 0, &e); + + if (lock == TRUE) { + nfs_rw_exit(&mi->mi_recovlock); + if (sp != NULL) + nfs_rw_exit(&sp->s_recovlock); + } + + nfscl = zone_getspecific(nfs4clnt_zone_key, nfs_zone()); + nfscl->nfscl_stat.referrals.value.ui64++; + DTRACE_PROBE3(nfs4clnt__func__referral__fsloc, + nfs4_sharedfh_t *, sfh, char *, nm, nfs4_error_t *, &e); + + if (e.error != 0) { + if (sp != NULL) + nfs4_server_rele(sp); + kmem_free(argop, argoplist_size); + return (0); + } + + /* + * Check for all possible error conditions. + * For valid replies without an ops array or for illegal + * replies, return a failure. + */ + if (res.status != NFS4_OK || res.array_len < 3 || + res.array[2].nfs_resop4_u.opgetattr.status != NFS4_OK) { + retval = 0; + goto exit; + } + + /* + * There isn't much value in putting the attributes + * in the attr cache since fs_locations4 aren't + * encountered very frequently, so just make them + * available to the caller. + */ + *garp = res.array[2].nfs_resop4_u.opgetattr.ga_res; + + DTRACE_PROBE2(nfs4clnt__debug__referral__fsloc, + nfs4_ga_res_t *, garp, char *, "nfs4_fetch_locations"); + + /* No fs_locations? -- return a failure */ + if (garp->n4g_ext_res == NULL || + garp->n4g_ext_res->n4g_fslocations.locations_val == NULL) { + retval = 0; + goto exit; + } + + if (!garp->n4g_fsid_valid) + retval = 0; + +exit: + if (retval == 0) { + /* the call was ok but failed validating the call results */ + (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); + } else { + ASSERT(callres != NULL); + *callres = res; + } + + if (sp != NULL) + nfs4_server_rele(sp); + kmem_free(argop, argoplist_size); + return (retval); +} + +/* tunable to disable referral mounts */ +int nfs4_no_referrals = 0; + +/* + * Returns NULL if the vnode cannot be created or found. + */ +vnode_t * +find_referral_stubvp(vnode_t *dvp, char *nm, cred_t *cr) +{ + nfs_fh4 *stub_fh, *dfh; + nfs4_sharedfh_t *sfhp; + char *newfhval; + vnode_t *vp = NULL; + fattr4_mounted_on_fileid mnt_on_fileid; + nfs4_ga_res_t garp; + mntinfo4_t *mi; + COMPOUND4res_clnt callres; + hrtime_t t; + + if (nfs4_no_referrals) + return (NULL); + + /* + * Get the mounted_on_fileid, unique on that server::fsid + */ + mi = VTOMI4(dvp); + if (nfs4_fetch_locations(mi, VTOR4(dvp)->r_fh, nm, cr, + &garp, &callres, FALSE) == 0) + return (NULL); + mnt_on_fileid = garp.n4g_mon_fid; + (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&callres); + + /* + * Build a fake filehandle from the dir FH and the mounted_on_fileid + */ + dfh = &VTOR4(dvp)->r_fh->sfh_fh; + stub_fh = kmem_alloc(sizeof (nfs_fh4), KM_SLEEP); + stub_fh->nfs_fh4_val = kmem_alloc(dfh->nfs_fh4_len + + sizeof (fattr4_mounted_on_fileid), KM_SLEEP); + newfhval = stub_fh->nfs_fh4_val; + + /* copy directory's file handle */ + bcopy(dfh->nfs_fh4_val, newfhval, dfh->nfs_fh4_len); + stub_fh->nfs_fh4_len = dfh->nfs_fh4_len; + newfhval = newfhval + dfh->nfs_fh4_len; + + /* Add mounted_on_fileid. Use bcopy to avoid alignment problem */ + bcopy((char *)&mnt_on_fileid, newfhval, + sizeof (fattr4_mounted_on_fileid)); + stub_fh->nfs_fh4_len += sizeof (fattr4_mounted_on_fileid); + + sfhp = sfh4_put(stub_fh, VTOMI4(dvp), NULL); + kmem_free(stub_fh->nfs_fh4_val, dfh->nfs_fh4_len + + sizeof (fattr4_mounted_on_fileid)); + kmem_free(stub_fh, sizeof (nfs_fh4)); + if (sfhp == NULL) + return (NULL); + + t = gethrtime(); + garp.n4g_va.va_type = VDIR; + vp = makenfs4node(sfhp, NULL, dvp->v_vfsp, t, + cr, dvp, fn_get(VTOSV(dvp)->sv_name, nm, sfhp)); + + if (vp != NULL) + vp->v_type = VDIR; + + sfh4_rele(&sfhp); + return (vp); +} + +int +nfs4_setup_referral(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr) +{ + vnode_t *nvp; + rnode4_t *rp; + + if ((nvp = find_referral_stubvp(dvp, nm, cr)) == NULL) + return (EINVAL); + + rp = VTOR4(nvp); + mutex_enter(&rp->r_statelock); + r4_stub_referral(rp); + mutex_exit(&rp->r_statelock); + dnlc_enter(dvp, nm, nvp); + + if (*vpp != NULL) + VN_RELE(*vpp); /* no longer need this vnode */ + + *vpp = nvp; + + return (0); +} + +/* + * Fetch the location information and resolve the new server. + * Caller needs to free up the XDR data which is returned. + * Input: mount info, shared filehandle, nodename + * Return: Index to the result or Error(-1) + * Output: FsLocations Info, Resolved Server Info. + */ +int +nfs4_process_referral(mntinfo4_t *mi, nfs4_sharedfh_t *sfh, + char *nm, cred_t *cr, nfs4_ga_res_t *grp, COMPOUND4res_clnt *res, + struct nfs_fsl_info *fsloc) +{ + fs_location4 *fsp; + struct nfs_fsl_info nfsfsloc; + int ret, i, error; + nfs4_ga_res_t garp; + COMPOUND4res_clnt callres; + struct knetconfig *knc; + + ret = nfs4_fetch_locations(mi, sfh, nm, cr, &garp, &callres, TRUE); + if (ret == 0) + return (-1); + + /* + * As a lame attempt to figuring out if we're + * handling a migration event or a referral, + * look for rnodes with this fsid in the rnode + * cache. + * + * If we can find one or more such rnodes, it + * means we're handling a migration event and + * we want to bail out in that case. + */ + if (r4find_by_fsid(mi, &garp.n4g_fsid)) { + DTRACE_PROBE3(nfs4clnt__debug__referral__migration, + mntinfo4_t *, mi, nfs4_ga_res_t *, &garp, + char *, "nfs4_process_referral"); + (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&callres); + return (-1); + } + + /* + * Find the first responsive server to mount. When we find + * one, fsp will point to it. + */ + for (i = 0; i < garp.n4g_ext_res->n4g_fslocations.locations_len; i++) { + + fsp = &garp.n4g_ext_res->n4g_fslocations.locations_val[i]; + if (fsp->server_len == 0 || fsp->server_val == NULL) + continue; + + error = nfs4_callmapid(fsp->server_val, &nfsfsloc); + if (error != 0) + continue; + + error = nfs4_ping_server_common(nfsfsloc.knconf, + nfsfsloc.addr, !(mi->mi_flags & MI4_INT)); + if (error == RPC_SUCCESS) + break; + + DTRACE_PROBE2(nfs4clnt__debug__referral__srvaddr, + sockaddr_in *, (struct sockaddr_in *)nfsfsloc.addr->buf, + char *, "nfs4_process_referral"); + + (void) xdr_free(xdr_nfs_fsl_info, (char *)&nfsfsloc); + } + knc = nfsfsloc.knconf; + if ((i >= garp.n4g_ext_res->n4g_fslocations.locations_len) || + (knc->knc_protofmly == NULL) || (knc->knc_proto == NULL)) { + DTRACE_PROBE2(nfs4clnt__debug__referral__nofsloc, + nfs4_ga_res_t *, &garp, char *, "nfs4_process_referral"); + (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&callres); + return (-1); + } + + /* Send the results back */ + *fsloc = nfsfsloc; + *grp = garp; + *res = callres; + return (i); +} + +/* + * Referrals case - need to fetch referral data and then upcall to + * user-level to get complete mount data. + */ +static ephemeral_servinfo_t * +nfs4_trigger_esi_create_referral(vnode_t *vp, cred_t *cr) +{ + struct knetconfig *sikncp, *svkncp; + struct netbuf *bufp; + ephemeral_servinfo_t *esi; + vnode_t *dvp; + rnode4_t *drp; + fs_location4 *fsp; + struct nfs_fsl_info nfsfsloc; + nfs4_ga_res_t garp; + char *p; + char fn[MAXNAMELEN]; + int i, index = -1; + mntinfo4_t *mi; + COMPOUND4res_clnt callres; + + /* + * If we're passed in a stub vnode that + * isn't a "referral" stub, bail out + * and return a failure + */ + if (!RP_ISSTUB_REFERRAL(VTOR4(vp))) + return (NULL); + + if (vtodv(vp, &dvp, CRED(), TRUE) != 0) + return (NULL); + + drp = VTOR4(dvp); + if (nfs_rw_enter_sig(&drp->r_rwlock, RW_READER, INTR4(dvp))) { + VN_RELE(dvp); + return (NULL); + } + + if (vtoname(vp, fn, MAXNAMELEN) != 0) { + nfs_rw_exit(&drp->r_rwlock); + VN_RELE(dvp); + return (NULL); + } + + mi = VTOMI4(dvp); + index = nfs4_process_referral(mi, drp->r_fh, fn, cr, + &garp, &callres, &nfsfsloc); + nfs_rw_exit(&drp->r_rwlock); + VN_RELE(dvp); + if (index < 0) + return (NULL); + + fsp = &garp.n4g_ext_res->n4g_fslocations.locations_val[index]; + esi = kmem_zalloc(sizeof (ephemeral_servinfo_t), KM_SLEEP); + + /* initially set to be our type of ephemeral mount; may be added to */ + esi->esi_mount_flags = NFSMNT_REFERRAL; + + esi->esi_hostname = + kmem_zalloc(fsp->server_val->utf8string_len + 1, KM_SLEEP); + bcopy(fsp->server_val->utf8string_val, esi->esi_hostname, + fsp->server_val->utf8string_len); + esi->esi_hostname[fsp->server_val->utf8string_len] = '\0'; + + bufp = kmem_alloc(sizeof (struct netbuf), KM_SLEEP); + bufp->len = nfsfsloc.addr->len; + bufp->maxlen = nfsfsloc.addr->maxlen; + bufp->buf = kmem_zalloc(bufp->len, KM_SLEEP); + bcopy(nfsfsloc.addr->buf, bufp->buf, bufp->len); + esi->esi_addr = bufp; + + esi->esi_knconf = kmem_zalloc(sizeof (*esi->esi_knconf), KM_SLEEP); + sikncp = esi->esi_knconf; + + DTRACE_PROBE2(nfs4clnt__debug__referral__nfsfsloc, + struct nfs_fsl_info *, &nfsfsloc, + char *, "nfs4_trigger_esi_create_referral"); + + svkncp = nfsfsloc.knconf; + sikncp->knc_semantics = svkncp->knc_semantics; + sikncp->knc_protofmly = (caddr_t)kmem_zalloc(KNC_STRSIZE, KM_SLEEP); + (void) strlcat((char *)sikncp->knc_protofmly, + (char *)svkncp->knc_protofmly, KNC_STRSIZE); + sikncp->knc_proto = (caddr_t)kmem_zalloc(KNC_STRSIZE, KM_SLEEP); + (void) strlcat((char *)sikncp->knc_proto, (char *)svkncp->knc_proto, + KNC_STRSIZE); + sikncp->knc_rdev = svkncp->knc_rdev; + + DTRACE_PROBE2(nfs4clnt__debug__referral__knetconf, + struct knetconfig *, sikncp, + char *, "nfs4_trigger_esi_create_referral"); + + esi->esi_netname = kmem_zalloc(nfsfsloc.netnm_len, KM_SLEEP); + bcopy(nfsfsloc.netname, esi->esi_netname, nfsfsloc.netnm_len); + esi->esi_syncaddr = NULL; + + esi->esi_path = p = kmem_zalloc(MAXPATHLEN, KM_SLEEP); + esi->esi_path_len = MAXPATHLEN; + *p++ = '/'; + for (i = 0; i < fsp->rootpath.pathname4_len; i++) { + component4 *comp; + + comp = &fsp->rootpath.pathname4_val[i]; + /* If no space, null the string and bail */ + if ((p - esi->esi_path) + comp->utf8string_len + 1 > MAXPATHLEN) + goto err; + bcopy(comp->utf8string_val, p, comp->utf8string_len); + p += comp->utf8string_len; + *p++ = '/'; + } + if (fsp->rootpath.pathname4_len != 0) + *(p - 1) = '\0'; + else + *p = '\0'; + p = esi->esi_path; + esi->esi_path = strdup(p); + esi->esi_path_len = strlen(p) + 1; + kmem_free(p, MAXPATHLEN); + + /* Allocated in nfs4_process_referral() */ + (void) xdr_free(xdr_nfs_fsl_info, (char *)&nfsfsloc); + (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&callres); + + return (esi); +err: + kmem_free(esi->esi_path, esi->esi_path_len); + kmem_free(esi->esi_hostname, fsp->server_val->utf8string_len + 1); + kmem_free(esi->esi_addr->buf, esi->esi_addr->len); + kmem_free(esi->esi_addr, sizeof (struct netbuf)); + kmem_free(esi->esi_knconf->knc_protofmly, KNC_STRSIZE); + kmem_free(esi->esi_knconf->knc_proto, KNC_STRSIZE); + kmem_free(esi->esi_knconf, sizeof (*esi->esi_knconf)); + kmem_free(esi->esi_netname, nfsfsloc.netnm_len); + kmem_free(esi, sizeof (ephemeral_servinfo_t)); + (void) xdr_free(xdr_nfs_fsl_info, (char *)&nfsfsloc); + (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&callres); + return (NULL); +} + +/* * Assemble the args, and call the generic VFS mount function to * finally perform the ephemeral mount. */ @@ -1357,6 +2011,9 @@ nfs4_trigger_nargs_create(mntinfo4_t *mi, servinfo4_t *svp, nargs->acdirmin = HR2SEC(mi->mi_acdirmin); nargs->acdirmax = HR2SEC(mi->mi_acdirmax); + /* add any specific flags for this type of ephemeral mount */ + nargs->flags |= esi->esi_mount_flags; + if (mi->mi_flags & MI4_NOCTO) nargs->flags |= NFSMNT_NOCTO; if (mi->mi_flags & MI4_GRPID) @@ -1367,19 +2024,28 @@ nfs4_trigger_nargs_create(mntinfo4_t *mi, servinfo4_t *svp, nargs->flags |= NFSMNT_NOPRINT; if (mi->mi_flags & MI4_DIRECTIO) nargs->flags |= NFSMNT_DIRECTIO; - if (mi->mi_flags & MI4_PUBLIC) + if (mi->mi_flags & MI4_PUBLIC && nargs->flags & NFSMNT_MIRRORMOUNT) nargs->flags |= NFSMNT_PUBLIC; - mutex_exit(&mi->mi_lock); + /* Do some referral-specific option tweaking */ + if (nargs->flags & NFSMNT_REFERRAL) { + nargs->flags &= ~NFSMNT_DORDMA; + nargs->flags |= NFSMNT_TRYRDMA; + } - /* add any specific flags for this type of ephemeral mount */ - nargs->flags |= esi->esi_mount_flags; + mutex_exit(&mi->mi_lock); /* * Security data & negotiation policy. * - * We need to preserve the parent mount's preference for security - * negotiation, translating SV4_TRYSECDEFAULT -> NFSMNT_SECDEFAULT. + * For mirror mounts, we need to preserve the parent mount's + * preference for security negotiation, translating SV4_TRYSECDEFAULT + * to NFSMNT_SECDEFAULT if present. + * + * For referrals, we always want security negotiation and will + * set NFSMNT_SECDEFAULT and we will not copy current secdata. + * The reason is that we can't negotiate down from a parent's + * Kerberos flavor to AUTH_SYS. * * If SV4_TRYSECDEFAULT is not set, that indicates that a specific * security flavour was requested, with data in sv_secdata, and that @@ -1395,8 +2061,16 @@ nfs4_trigger_nargs_create(mntinfo4_t *mi, servinfo4_t *svp, * we will copy sv_currsec. Otherwise, copy sv_secdata. Regardless, * we will set NFSMNT_SECDEFAULT, to enable negotiation. */ - if (svp->sv_flags & SV4_TRYSECDEFAULT) { - /* enable negotiation for ephemeral mount */ + if (nargs->flags & NFSMNT_REFERRAL) { + /* enable negotiation for referral mount */ + nargs->flags |= NFSMNT_SECDEFAULT; + secdata = kmem_alloc(sizeof (sec_data_t), KM_SLEEP); + secdata->secmod = secdata->rpcflavor = AUTH_SYS; + secdata->data = NULL; + } + + else if (svp->sv_flags & SV4_TRYSECDEFAULT) { + /* enable negotiation for mirror mount */ nargs->flags |= NFSMNT_SECDEFAULT; /* @@ -1499,6 +2173,11 @@ nfs4_record_ephemeral_mount(mntinfo4_t *mi, vnode_t *mvp) return (EBUSY); } + /* + * We've just tied the mntinfo to the tree, so + * now we bump the refcnt and hold it there until + * this mntinfo is removed from the tree. + */ nfs4_ephemeral_tree_hold(net); /* @@ -1515,7 +2194,6 @@ nfs4_record_ephemeral_mount(mntinfo4_t *mi, vnode_t *mvp) */ eph->ne_mount_to = ntg->ntg_mount_to; - mi->mi_flags |= MI4_EPHEMERAL; mi->mi_ephemeral = eph; /* @@ -1542,6 +2220,7 @@ nfs4_record_ephemeral_mount(mntinfo4_t *mi, vnode_t *mvp) mi->mi_flags &= ~MI4_EPHEMERAL; mi->mi_ephemeral = NULL; kmem_free(eph, sizeof (*eph)); + nfs4_ephemeral_tree_rele(net); rc = EBUSY; } else { if (prior->ne_child == NULL) { @@ -1581,8 +2260,6 @@ nfs4_record_ephemeral_mount(mntinfo4_t *mi, vnode_t *mvp) eph->ne_prior = NULL; } - nfs4_ephemeral_tree_rele(net); - mutex_exit(&mi->mi_lock); mutex_exit(&mi_parent->mi_lock); @@ -1758,15 +2435,13 @@ nfs4_ephemeral_unmount_engine(nfs4_ephemeral_t *eph, */ void nfs4_ephemeral_umount_unlock(bool_t *pmust_unlock, - bool_t *pmust_rele, nfs4_ephemeral_tree_t **pnet) + nfs4_ephemeral_tree_t **pnet) { nfs4_ephemeral_tree_t *net = *pnet; if (*pmust_unlock) { mutex_enter(&net->net_cnt_lock); net->net_status &= ~NFS4_EPHEMERAL_TREE_UMOUNTING; - if (*pmust_rele) - nfs4_ephemeral_tree_decr(net); mutex_exit(&net->net_cnt_lock); mutex_exit(&net->net_tree_lock); @@ -1783,7 +2458,7 @@ nfs4_ephemeral_umount_unlock(bool_t *pmust_unlock, */ void nfs4_ephemeral_umount_activate(mntinfo4_t *mi, bool_t *pmust_unlock, - bool_t *pmust_rele, nfs4_ephemeral_tree_t **pnet) + nfs4_ephemeral_tree_t **pnet) { /* * Now we need to get rid of the ephemeral data if it exists. @@ -1798,6 +2473,7 @@ nfs4_ephemeral_umount_activate(mntinfo4_t *mi, bool_t *pmust_unlock, if (!(mi->mi_flags & MI4_EPHEMERAL_RECURSED)) nfs4_ephemeral_umount_cleanup(mi->mi_ephemeral); + nfs4_ephemeral_tree_rele(*pnet); ASSERT(mi->mi_ephemeral != NULL); kmem_free(mi->mi_ephemeral, sizeof (*mi->mi_ephemeral)); @@ -1805,15 +2481,19 @@ nfs4_ephemeral_umount_activate(mntinfo4_t *mi, bool_t *pmust_unlock, } mutex_exit(&mi->mi_lock); - nfs4_ephemeral_umount_unlock(pmust_unlock, pmust_rele, pnet); + nfs4_ephemeral_umount_unlock(pmust_unlock, pnet); } /* * Unmount an ephemeral node. + * + * Note that if this code fails, then it must unlock. + * + * If it succeeds, then the caller must be prepared to do so. */ int nfs4_ephemeral_umount(mntinfo4_t *mi, int flag, cred_t *cr, - bool_t *pmust_unlock, bool_t *pmust_rele, nfs4_ephemeral_tree_t **pnet) + bool_t *pmust_unlock, nfs4_ephemeral_tree_t **pnet) { int error = 0; nfs4_ephemeral_t *eph; @@ -1826,7 +2506,7 @@ nfs4_ephemeral_umount(mntinfo4_t *mi, int flag, cred_t *cr, * Make sure to set the default state for cleaning * up the tree in the caller (and on the way out). */ - *pmust_unlock = *pmust_rele = FALSE; + *pmust_unlock = FALSE; /* * The active vnodes on this file system may be ephemeral @@ -1865,16 +2545,18 @@ nfs4_ephemeral_umount(mntinfo4_t *mi, int flag, cred_t *cr, is_recursed = mi->mi_flags & MI4_EPHEMERAL_RECURSED; is_derooting = (eph == NULL); + mutex_enter(&net->net_cnt_lock); + /* * If this is not recursion, then we need to - * grab a ref count. + * check to see if a harvester thread has + * already grabbed the lock. * - * But wait, we also do not want to do that - * if a harvester thread has already grabbed - * the lock. + * After we exit this branch, we may not + * blindly return, we need to jump to + * is_busy! */ if (!is_recursed) { - mutex_enter(&net->net_cnt_lock); if (net->net_status & NFS4_EPHEMERAL_TREE_LOCKED) { /* @@ -1902,13 +2584,10 @@ nfs4_ephemeral_umount(mntinfo4_t *mi, int flag, cred_t *cr, } was_locked = TRUE; - } else { - nfs4_ephemeral_tree_incr(net); - *pmust_rele = TRUE; } - - mutex_exit(&net->net_cnt_lock); } + + mutex_exit(&net->net_cnt_lock); mutex_exit(&mi->mi_lock); /* @@ -1936,9 +2615,7 @@ nfs4_ephemeral_umount(mntinfo4_t *mi, int flag, cred_t *cr, if (net->net_status & (NFS4_EPHEMERAL_TREE_DEROOTING | NFS4_EPHEMERAL_TREE_INVALID)) { - nfs4_ephemeral_tree_decr(net); mutex_exit(&net->net_cnt_lock); - *pmust_rele = FALSE; goto is_busy; } mutex_exit(&net->net_cnt_lock); @@ -1974,10 +2651,8 @@ nfs4_ephemeral_umount(mntinfo4_t *mi, int flag, cred_t *cr, if (net->net_status & NFS4_EPHEMERAL_TREE_INVALID || (!is_derooting && eph == NULL)) { - nfs4_ephemeral_tree_decr(net); mutex_exit(&net->net_cnt_lock); mutex_exit(&net->net_tree_lock); - *pmust_rele = FALSE; goto is_busy; } mutex_exit(&net->net_cnt_lock); @@ -2048,8 +2723,14 @@ nfs4_ephemeral_umount(mntinfo4_t *mi, int flag, cred_t *cr, mutex_enter(&net->net_cnt_lock); net->net_status &= ~NFS4_EPHEMERAL_TREE_DEROOTING; net->net_status |= NFS4_EPHEMERAL_TREE_INVALID; - if (was_locked == FALSE) - nfs4_ephemeral_tree_decr(net); + DTRACE_NFSV4_1(nfs4clnt__dbg__ephemeral__tree__derooting, + uint_t, net->net_refcnt); + + /* + * We will not finalize this node, so safe to + * release it. + */ + nfs4_ephemeral_tree_decr(net); mutex_exit(&net->net_cnt_lock); if (was_locked == FALSE) @@ -2057,8 +2738,8 @@ nfs4_ephemeral_umount(mntinfo4_t *mi, int flag, cred_t *cr, /* * We have just blown away any notation of this - * tree being locked. We can't let the caller - * try to clean things up. + * tree being locked or having a refcnt. + * We can't let the caller try to clean things up. */ *pmust_unlock = FALSE; @@ -2077,8 +2758,7 @@ nfs4_ephemeral_umount(mntinfo4_t *mi, int flag, cred_t *cr, is_busy: - nfs4_ephemeral_umount_unlock(pmust_unlock, pmust_rele, - pnet); + nfs4_ephemeral_umount_unlock(pmust_unlock, pnet); return (error); } @@ -2314,19 +2994,18 @@ check_done: /* * At this point we are done processing this tree. * - * If the tree is invalid and we are the only reference + * If the tree is invalid and we were the only reference * to it, then we push it on the local linked list * to remove it at the end. We avoid that action now * to keep the tree processing going along at a fair clip. * - * Else, even if we are the only reference, we drop - * our hold on the current tree and allow it to be - * reused as needed. + * Else, even if we were the only reference, we + * allow it to be reused as needed. */ mutex_enter(&net->net_cnt_lock); - if (net->net_refcnt == 1 && + nfs4_ephemeral_tree_decr(net); + if (net->net_refcnt == 0 && net->net_status & NFS4_EPHEMERAL_TREE_INVALID) { - nfs4_ephemeral_tree_decr(net); net->net_status &= ~NFS4_EPHEMERAL_TREE_LOCKED; mutex_exit(&net->net_cnt_lock); mutex_exit(&net->net_tree_lock); @@ -2341,7 +3020,6 @@ check_done: continue; } - nfs4_ephemeral_tree_decr(net); net->net_status &= ~NFS4_EPHEMERAL_TREE_LOCKED; mutex_exit(&net->net_cnt_lock); mutex_exit(&net->net_tree_lock); @@ -2620,9 +3298,9 @@ nfs4_trigger_add_mntopt(char *mntopts, char *optname, vfs_t *vfsp) } static enum clnt_stat -nfs4_trigger_ping_server(servinfo4_t *svp, int nointr) +nfs4_ping_server_common(struct knetconfig *knc, struct netbuf *addr, int nointr) { - int retries, error; + int retries; uint_t max_msgsize; enum clnt_stat status; CLIENT *cl; @@ -2634,9 +3312,8 @@ nfs4_trigger_ping_server(servinfo4_t *svp, int nointr) timeout.tv_sec = 2; timeout.tv_usec = 0; - error = clnt_tli_kcreate(svp->sv_knconf, &svp->sv_addr, NFS_PROGRAM, - NFS_V4, max_msgsize, retries, CRED(), &cl); - if (error) + if (clnt_tli_kcreate(knc, addr, NFS_PROGRAM, NFS_V4, + max_msgsize, retries, CRED(), &cl) != 0) return (RPC_FAILED); if (nointr) @@ -2651,3 +3328,9 @@ nfs4_trigger_ping_server(servinfo4_t *svp, int nointr) return (status); } + +static enum clnt_stat +nfs4_trigger_ping_server(servinfo4_t *svp, int nointr) +{ + return (nfs4_ping_server_common(svp->sv_knconf, &svp->sv_addr, nointr)); +} diff --git a/usr/src/uts/common/fs/nfs/nfs4_subr.c b/usr/src/uts/common/fs/nfs/nfs4_subr.c index 27261bf583..b94d41899d 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_subr.c +++ b/usr/src/uts/common/fs/nfs/nfs4_subr.c @@ -59,6 +59,8 @@ static const struct clstat4 clstat4_tmpl = { { "calls", KSTAT_DATA_UINT64 }, { "badcalls", KSTAT_DATA_UINT64 }, + { "referrals", KSTAT_DATA_UINT64 }, + { "referlinks", KSTAT_DATA_UINT64 }, { "clgets", KSTAT_DATA_UINT64 }, { "cltoomany", KSTAT_DATA_UINT64 }, #ifdef DEBUG @@ -90,7 +92,7 @@ struct clstat4_debug clstat4_debug = { */ static list_t nfs4_clnt_list; static kmutex_t nfs4_clnt_list_lock; -static zone_key_t nfs4clnt_zone_key; +zone_key_t nfs4clnt_zone_key; static struct kmem_cache *chtab4_cache; @@ -1943,6 +1945,9 @@ again: * crossed an underlying server fs boundary. * * This stub will be for a mirror-mount. + * A referral would look like a boundary crossing + * as well, but would not be the same type of object, + * so we would expect to mark the object dead. * * See comment in r4_do_attrcache() for more details. */ @@ -2109,7 +2114,8 @@ recov_retry: bool_t abort; abort = nfs4_start_recovery(&e, mi, - rootvp, NULL, NULL, NULL, OP_LOOKUP, NULL); + rootvp, NULL, NULL, NULL, OP_LOOKUP, NULL, NULL, + NULL); if (abort) { nfs4_end_fop(mi, rootvp, NULL, OH_LOOKUP, &recov_state, FALSE); @@ -2169,7 +2175,7 @@ recov_retry: abort = nfs4_start_recovery(&e, mi, rootvp, NULL, NULL, NULL, - OP_LOOKUP, NULL); + OP_LOOKUP, NULL, NULL, NULL); if (abort) { nfs4_end_fop(mi, rootvp, NULL, OH_LOOKUP, &recov_state, @@ -2757,6 +2763,7 @@ clinit4_zone(zoneid_t zoneid) mutex_enter(&nfs4_clnt_list_lock); list_insert_head(&nfs4_clnt_list, nfscl); mutex_exit(&nfs4_clnt_list_lock); + return (nfscl); } diff --git a/usr/src/uts/common/fs/nfs/nfs4_vfsops.c b/usr/src/uts/common/fs/nfs/nfs4_vfsops.c index 71bb6f7f2f..2ebd9d6fdd 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_vfsops.c +++ b/usr/src/uts/common/fs/nfs/nfs4_vfsops.c @@ -76,6 +76,8 @@ #include <nfs/nfs4_clnt.h> #include <sys/fs/autofs.h> +#include <sys/sdt.h> + /* * Arguments passed to thread to free data structures from forced unmount. @@ -160,6 +162,14 @@ static void remove_mi(nfs4_server_t *, mntinfo4_t *); extern void nfs4_ephemeral_init(void); extern void nfs4_ephemeral_fini(void); +/* referral related routines */ +static servinfo4_t *copy_svp(servinfo4_t *); +static void free_knconf_contents(struct knetconfig *k); +static char *extract_referral_point(const char *, int); +static void setup_newsvpath(servinfo4_t *, int); +static void update_servinfo4(servinfo4_t *, fs_location4 *, + struct nfs_fsl_info *, char *, int); + /* * Initialize the vfs structure */ @@ -1270,7 +1280,7 @@ recov_retry: "getlinktext_otw: initiating recovery\n")); if (nfs4_start_recovery(&e, mi, NULL, NULL, NULL, NULL, - OP_READLINK, NULL) == FALSE) { + OP_READLINK, NULL, NULL, NULL) == FALSE) { nfs4_end_op(mi, NULL, NULL, &recov_state, needrecov); if (!e.error) (void) xdr_free(xdr_COMPOUND4res_clnt, @@ -1457,6 +1467,198 @@ out: } /* + * This routine updates servinfo4 structure with the new referred server + * info. + * nfsfsloc has the location related information + * fsp has the hostname and pathname info. + * new path = pathname from referral + part of orig pathname(based on nth). + */ +static void +update_servinfo4(servinfo4_t *svp, fs_location4 *fsp, + struct nfs_fsl_info *nfsfsloc, char *orig_path, int nth) +{ + struct knetconfig *knconf, *svknconf; + struct netbuf *saddr; + sec_data_t *secdata; + utf8string *host; + int i = 0, num_slashes = 0; + char *p, *spath, *op, *new_path; + + /* Update knconf */ + knconf = svp->sv_knconf; + free_knconf_contents(knconf); + bzero(knconf, sizeof (struct knetconfig)); + svknconf = nfsfsloc->knconf; + knconf->knc_semantics = svknconf->knc_semantics; + knconf->knc_protofmly = kmem_zalloc(KNC_STRSIZE, KM_SLEEP); + knconf->knc_proto = kmem_zalloc(KNC_STRSIZE, KM_SLEEP); + knconf->knc_rdev = svknconf->knc_rdev; + bcopy(svknconf->knc_protofmly, knconf->knc_protofmly, KNC_STRSIZE); + bcopy(svknconf->knc_proto, knconf->knc_proto, KNC_STRSIZE); + + /* Update server address */ + saddr = &svp->sv_addr; + if (saddr->buf != NULL) + kmem_free(saddr->buf, saddr->maxlen); + saddr->buf = kmem_alloc(nfsfsloc->addr->maxlen, KM_SLEEP); + saddr->len = nfsfsloc->addr->len; + saddr->maxlen = nfsfsloc->addr->maxlen; + bcopy(nfsfsloc->addr->buf, saddr->buf, nfsfsloc->addr->len); + + /* Update server name */ + host = fsp->server_val; + kmem_free(svp->sv_hostname, svp->sv_hostnamelen); + svp->sv_hostname = kmem_zalloc(host->utf8string_len + 1, KM_SLEEP); + bcopy(host->utf8string_val, svp->sv_hostname, host->utf8string_len); + svp->sv_hostname[host->utf8string_len] = '\0'; + svp->sv_hostnamelen = host->utf8string_len + 1; + + /* + * Update server path. + * We need to setup proper path here. + * For ex., If we got a path name serv1:/rp/aaa/bbb + * where aaa is a referral and points to serv2:/rpool/aa + * we need to set the path to serv2:/rpool/aa/bbb + * The first part of this below code generates /rpool/aa + * and the second part appends /bbb to the server path. + */ + spath = p = kmem_zalloc(MAXPATHLEN, KM_SLEEP); + *p++ = '/'; + for (i = 0; i < fsp->rootpath.pathname4_len; i++) { + component4 *comp; + + comp = &fsp->rootpath.pathname4_val[i]; + /* If no space, null the string and bail */ + if ((p - spath) + comp->utf8string_len + 1 > MAXPATHLEN) { + p = spath + MAXPATHLEN - 1; + spath[0] = '\0'; + break; + } + bcopy(comp->utf8string_val, p, comp->utf8string_len); + p += comp->utf8string_len; + *p++ = '/'; + } + if (fsp->rootpath.pathname4_len != 0) + *(p - 1) = '\0'; + else + *p = '\0'; + p = spath; + + new_path = kmem_zalloc(MAXPATHLEN, KM_SLEEP); + (void) strlcpy(new_path, p, MAXPATHLEN); + kmem_free(p, MAXPATHLEN); + i = strlen(new_path); + + for (op = orig_path; *op; op++) { + if (*op == '/') + num_slashes++; + if (num_slashes == nth + 2) { + while (*op != '\0') { + new_path[i] = *op; + i++; + op++; + } + break; + } + } + new_path[i] = '\0'; + + kmem_free(svp->sv_path, svp->sv_pathlen); + svp->sv_pathlen = strlen(new_path) + 1; + svp->sv_path = kmem_alloc(svp->sv_pathlen, KM_SLEEP); + bcopy(new_path, svp->sv_path, svp->sv_pathlen); + kmem_free(new_path, MAXPATHLEN); + + /* + * All the security data is specific to old server. + * Clean it up except secdata which deals with mount options. + * We need to inherit that data. Copy secdata into our new servinfo4. + */ + if (svp->sv_dhsec) { + sec_clnt_freeinfo(svp->sv_dhsec); + svp->sv_dhsec = NULL; + } + if (svp->sv_save_secinfo && + svp->sv_save_secinfo != svp->sv_secinfo) { + secinfo_free(svp->sv_save_secinfo); + svp->sv_save_secinfo = NULL; + } + if (svp->sv_secinfo) { + secinfo_free(svp->sv_secinfo); + svp->sv_secinfo = NULL; + } + svp->sv_currsec = NULL; + + secdata = kmem_alloc(sizeof (*secdata), KM_SLEEP); + *secdata = *svp->sv_secdata; + secdata->data = NULL; + if (svp->sv_secdata) { + sec_clnt_freeinfo(svp->sv_secdata); + svp->sv_secdata = NULL; + } + svp->sv_secdata = secdata; +} + +/* + * Resolve a referral. The referral is in the n+1th component of + * svp->sv_path and has a parent nfs4 file handle "fh". + * Upon return, the sv_path will point to the new path that has referral + * component resolved to its referred path and part of original path. + * Hostname and other address information is also updated. + */ +int +resolve_referral(mntinfo4_t *mi, servinfo4_t *svp, cred_t *cr, int nth, + nfs_fh4 *fh) +{ + nfs4_sharedfh_t *sfh; + struct nfs_fsl_info nfsfsloc; + nfs4_ga_res_t garp; + COMPOUND4res_clnt callres; + fs_location4 *fsp; + char *nm, *orig_path; + int orig_pathlen = 0, ret = -1, index; + + if (svp->sv_pathlen <= 0) + return (ret); + + (void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0); + orig_pathlen = svp->sv_pathlen; + orig_path = kmem_alloc(orig_pathlen, KM_SLEEP); + bcopy(svp->sv_path, orig_path, orig_pathlen); + nm = extract_referral_point(svp->sv_path, nth); + setup_newsvpath(svp, nth); + nfs_rw_exit(&svp->sv_lock); + + sfh = sfh4_get(fh, mi); + index = nfs4_process_referral(mi, sfh, nm, cr, + &garp, &callres, &nfsfsloc); + sfh4_rele(&sfh); + kmem_free(nm, MAXPATHLEN); + if (index < 0) { + kmem_free(orig_path, orig_pathlen); + return (index); + } + + fsp = &garp.n4g_ext_res->n4g_fslocations.locations_val[index]; + (void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0); + update_servinfo4(svp, fsp, &nfsfsloc, orig_path, nth); + nfs_rw_exit(&svp->sv_lock); + + mutex_enter(&mi->mi_lock); + mi->mi_vfs_referral_loop_cnt++; + mutex_exit(&mi->mi_lock); + + ret = 0; +bad: + /* Free up XDR memory allocated in nfs4_process_referral() */ + xdr_free(xdr_nfs_fsl_info, (char *)&nfsfsloc); + xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&callres); + kmem_free(orig_path, orig_pathlen); + + return (ret); +} + +/* * Get the root filehandle for the given filesystem and server, and update * svp. * @@ -1466,7 +1668,6 @@ out: * * Errors are returned by the nfs4_error_t parameter. */ - static void nfs4getfh_otw(struct mntinfo4 *mi, servinfo4_t *svp, vtype_t *vtp, int flags, cred_t *cr, nfs4_error_t *ep) @@ -1498,7 +1699,14 @@ nfs4getfh_otw(struct mntinfo4 *mi, servinfo4_t *svp, vtype_t *vtp, recov_state.rs_flags = 0; recov_state.rs_num_retry_despite_err = 0; + recov_retry: + if (mi->mi_vfs_referral_loop_cnt >= NFS4_REFERRAL_LOOP_MAX) { + DTRACE_PROBE3(nfs4clnt__debug__referral__loop, mntinfo4 *, + mi, servinfo4_t *, svp, char *, "nfs4getfh_otw"); + nfs4_error_init(ep, EINVAL); + return; + } nfs4_error_zinit(ep); if (!recovery) { @@ -1599,7 +1807,7 @@ recov_retry: (CE_NOTE, "nfs4getfh_otw: initiating recovery\n")); abort = nfs4_start_recovery(ep, mi, NULL, - NULL, NULL, NULL, OP_GETFH, NULL); + NULL, NULL, NULL, OP_GETFH, NULL, NULL, NULL); if (!ep->error) { ep->error = geterrno4(res.status); (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); @@ -1628,7 +1836,8 @@ recov_retry: is_link_err: /* for non-recovery errors */ - if (res.status && res.status != NFS4ERR_SYMLINK) { + if (res.status && res.status != NFS4ERR_SYMLINK && + res.status != NFS4ERR_MOVED) { if (!recovery) { nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state, needrecov); @@ -1643,10 +1852,18 @@ is_link_err: * If any intermediate component in the path is a symbolic link, * resolve the symlink, then try mount again using the new path. */ - if (res.status == NFS4ERR_SYMLINK) { + if (res.status == NFS4ERR_SYMLINK || res.status == NFS4ERR_MOVED) { int where; /* + * Need to call nfs4_end_op before resolve_sympath to avoid + * potential nfs4_start_op deadlock. + */ + if (!recovery) + nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state, + needrecov); + + /* * This must be from OP_LOOKUP failure. The (cfh) for this * OP_LOOKUP is a symlink node. Found out where the * OP_GETFH is for the (cfh) that is a symlink node. @@ -1661,21 +1878,24 @@ is_link_err: where = res.array_len - 2; ASSERT(where > 0); - resop = &res.array[where - 1]; - ASSERT(resop->resop == OP_GETFH); - tmpfhp = &resop->nfs_resop4_u.opgetfh.object; - nthcomp = res.array_len/3 - 1; + if (res.status == NFS4ERR_SYMLINK) { - /* - * Need to call nfs4_end_op before resolve_sympath to avoid - * potential nfs4_start_op deadlock. - */ - if (!recovery) - nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state, - needrecov); + resop = &res.array[where - 1]; + ASSERT(resop->resop == OP_GETFH); + tmpfhp = &resop->nfs_resop4_u.opgetfh.object; + nthcomp = res.array_len/3 - 1; + ep->error = resolve_sympath(mi, svp, nthcomp, + tmpfhp, cr, flags); - ep->error = resolve_sympath(mi, svp, nthcomp, tmpfhp, cr, - flags); + } else if (res.status == NFS4ERR_MOVED) { + + resop = &res.array[where - 2]; + ASSERT(resop->resop == OP_GETFH); + tmpfhp = &resop->nfs_resop4_u.opgetfh.object; + nthcomp = res.array_len/3 - 1; + ep->error = resolve_referral(mi, svp, cr, nthcomp, + tmpfhp); + } nfs4args_lookup_free(argop, num_argops); kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4)); @@ -1809,7 +2029,6 @@ is_link_err: garp->n4g_ext_res->n4g_suppattrs | FATTR4_MANDATTR_MASK; nfs_rw_exit(&svp->sv_lock); - nfs4args_lookup_free(argop, num_argops); kmem_free(argop, lookuparg.arglen * sizeof (nfs_argop4)); (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); @@ -1817,6 +2036,128 @@ is_link_err: nfs4_end_fop(mi, NULL, NULL, OH_MOUNT, &recov_state, needrecov); } +/* + * Save a copy of Servinfo4_t structure. + * We might need when there is a failure in getting file handle + * in case of a referral to replace servinfo4 struct and try again. + */ +static struct servinfo4 * +copy_svp(servinfo4_t *nsvp) +{ + servinfo4_t *svp = NULL; + struct knetconfig *sknconf, *tknconf; + struct netbuf *saddr, *taddr; + + svp = kmem_zalloc(sizeof (*svp), KM_SLEEP); + nfs_rw_init(&svp->sv_lock, NULL, RW_DEFAULT, NULL); + svp->sv_flags = nsvp->sv_flags; + svp->sv_fsid = nsvp->sv_fsid; + svp->sv_hostnamelen = nsvp->sv_hostnamelen; + svp->sv_pathlen = nsvp->sv_pathlen; + svp->sv_supp_attrs = nsvp->sv_supp_attrs; + + svp->sv_path = kmem_alloc(svp->sv_pathlen, KM_SLEEP); + svp->sv_hostname = kmem_alloc(svp->sv_hostnamelen, KM_SLEEP); + bcopy(nsvp->sv_hostname, svp->sv_hostname, svp->sv_hostnamelen); + bcopy(nsvp->sv_path, svp->sv_path, svp->sv_pathlen); + + saddr = &nsvp->sv_addr; + taddr = &svp->sv_addr; + taddr->maxlen = saddr->maxlen; + taddr->len = saddr->len; + if (saddr->len > 0) { + taddr->buf = kmem_zalloc(saddr->maxlen, KM_SLEEP); + bcopy(saddr->buf, taddr->buf, saddr->len); + } + + svp->sv_knconf = kmem_zalloc(sizeof (struct knetconfig), KM_SLEEP); + sknconf = nsvp->sv_knconf; + tknconf = svp->sv_knconf; + tknconf->knc_semantics = sknconf->knc_semantics; + tknconf->knc_rdev = sknconf->knc_rdev; + if (sknconf->knc_proto != NULL) { + tknconf->knc_proto = kmem_zalloc(KNC_STRSIZE, KM_SLEEP); + bcopy(sknconf->knc_proto, (char *)tknconf->knc_proto, + KNC_STRSIZE); + } + if (sknconf->knc_protofmly != NULL) { + tknconf->knc_protofmly = kmem_zalloc(KNC_STRSIZE, KM_SLEEP); + bcopy(sknconf->knc_protofmly, (char *)tknconf->knc_protofmly, + KNC_STRSIZE); + } + + if (nsvp->sv_origknconf != NULL) { + svp->sv_origknconf = kmem_zalloc(sizeof (struct knetconfig), + KM_SLEEP); + sknconf = nsvp->sv_origknconf; + tknconf = svp->sv_origknconf; + tknconf->knc_semantics = sknconf->knc_semantics; + tknconf->knc_rdev = sknconf->knc_rdev; + if (sknconf->knc_proto != NULL) { + tknconf->knc_proto = kmem_zalloc(KNC_STRSIZE, KM_SLEEP); + bcopy(sknconf->knc_proto, (char *)tknconf->knc_proto, + KNC_STRSIZE); + } + if (sknconf->knc_protofmly != NULL) { + tknconf->knc_protofmly = kmem_zalloc(KNC_STRSIZE, + KM_SLEEP); + bcopy(sknconf->knc_protofmly, + (char *)tknconf->knc_protofmly, KNC_STRSIZE); + } + } + + svp->sv_secdata = copy_sec_data(nsvp->sv_secdata); + svp->sv_dhsec = copy_sec_data(svp->sv_dhsec); + /* + * Rest of the security information is not copied as they are built + * with the information available from secdata and dhsec. + */ + svp->sv_next = NULL; + + return (svp); +} + +servinfo4_t * +restore_svp(mntinfo4_t *mi, servinfo4_t *svp, servinfo4_t *origsvp) +{ + servinfo4_t *srvnext, *tmpsrv; + + if (strcmp(svp->sv_hostname, origsvp->sv_hostname) != 0) { + /* + * Since the hostname changed, we must be dealing + * with a referral, and the lookup failed. We will + * restore the whole servinfo4_t to what it was before. + */ + srvnext = svp->sv_next; + svp->sv_next = NULL; + tmpsrv = copy_svp(origsvp); + sv4_free(svp); + svp = tmpsrv; + svp->sv_next = srvnext; + mutex_enter(&mi->mi_lock); + mi->mi_servers = svp; + mi->mi_curr_serv = svp; + mutex_exit(&mi->mi_lock); + + } else if (origsvp->sv_pathlen != svp->sv_pathlen) { + + /* + * For symlink case: restore original path because + * it might have contained symlinks that were + * expanded by nfsgetfh_otw before the failure occurred. + */ + nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0); + kmem_free(svp->sv_path, svp->sv_pathlen); + svp->sv_path = + kmem_alloc(origsvp->sv_pathlen, KM_SLEEP); + svp->sv_pathlen = origsvp->sv_pathlen; + bcopy(origsvp->sv_path, svp->sv_path, + origsvp->sv_pathlen); + nfs_rw_exit(&svp->sv_lock); + } + return (svp); +} + static ushort_t nfs4_max_threads = 8; /* max number of active async threads */ static uint_t nfs4_bsize = 32 * 1024; /* client `block' size */ static uint_t nfs4_async_clusters = 1; /* # of reqs from each async queue */ @@ -1830,12 +2171,11 @@ static uint_t nfs4_cots_timeo = NFS_COTS_TIMEO; void nfs4_remap_root(mntinfo4_t *mi, nfs4_error_t *ep, int flags) { - struct servinfo4 *svp; + struct servinfo4 *svp, *origsvp; vtype_t vtype; nfs_fh4 rootfh; int getfh_flags; - char *orig_sv_path; - int orig_sv_pathlen, num_retry; + int num_retry; mutex_enter(&mi->mi_lock); @@ -1861,9 +2201,7 @@ remap_retry: * to re-lookup everything and recover. */ (void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0); - orig_sv_pathlen = svp->sv_pathlen; - orig_sv_path = kmem_alloc(orig_sv_pathlen, KM_SLEEP); - bcopy(svp->sv_path, orig_sv_path, orig_sv_pathlen); + origsvp = copy_svp(svp); nfs_rw_exit(&svp->sv_lock); num_retry = nfs4_max_mount_retry; @@ -1882,24 +2220,13 @@ remap_retry: /* * For some reason, the mount compound failed. Before - * retrying, we need to restore the original sv_path - * because it might have contained symlinks that were - * expanded by nfsgetfh_otw before the failure occurred. - * replace current sv_path with orig sv_path -- just in case - * it changed due to embedded symlinks. + * retrying, we need to restore original conditions. */ - (void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0); - if (orig_sv_pathlen != svp->sv_pathlen) { - kmem_free(svp->sv_path, svp->sv_pathlen); - svp->sv_path = kmem_alloc(orig_sv_pathlen, KM_SLEEP); - svp->sv_pathlen = orig_sv_pathlen; - } - bcopy(orig_sv_path, svp->sv_path, orig_sv_pathlen); - nfs_rw_exit(&svp->sv_lock); + svp = restore_svp(mi, svp, origsvp); } while (num_retry-- > 0); - kmem_free(orig_sv_path, orig_sv_pathlen); + sv4_free(origsvp); if (ep->error != 0 || ep->stat != 0) { return; @@ -1940,7 +2267,7 @@ nfs4rootvp(vnode_t **rtvpp, vfs_t *vfsp, struct servinfo4 *svp_head, dev_t nfs_dev; int error = 0; rnode4_t *rp; - int i; + int i, len; struct vattr va; vtype_t vtype = VNON; vtype_t tmp_vtype = VNON; @@ -1951,9 +2278,10 @@ nfs4rootvp(vnode_t **rtvpp, vfs_t *vfsp, struct servinfo4 *svp_head, struct nfs_stats *nfsstatsp; nfs4_fname_t *mfname; nfs4_error_t e; - char *orig_sv_path; - int orig_sv_pathlen, num_retry, removed; + int num_retry, removed; cred_t *lcr = NULL, *tcr = cr; + struct servinfo4 *origsvp; + char *resource; nfsstatsp = zone_getspecific(nfsstat_zone_key, nfs_zone()); ASSERT(nfsstatsp != NULL); @@ -1980,6 +2308,8 @@ nfs4rootvp(vnode_t **rtvpp, vfs_t *vfsp, struct servinfo4 *svp_head, mi->mi_flags |= MI4_PUBLIC; if (flags & NFSMNT_MIRRORMOUNT) mi->mi_flags |= MI4_MIRRORMOUNT; + if (flags & NFSMNT_REFERRAL) + mi->mi_flags |= MI4_REFERRAL; mi->mi_retrans = NFS_RETRIES; if (svp->sv_knconf->knc_semantics == NC_TPI_COTS_ORD || svp->sv_knconf->knc_semantics == NC_TPI_COTS) @@ -2100,9 +2430,7 @@ nfs4rootvp(vnode_t **rtvpp, vfs_t *vfsp, struct servinfo4 *svp_head, * Save server path we're attempting to mount. */ (void) nfs_rw_enter_sig(&svp->sv_lock, RW_WRITER, 0); - orig_sv_pathlen = svp_head->sv_pathlen; - orig_sv_path = kmem_alloc(svp_head->sv_pathlen, KM_SLEEP); - bcopy(svp_head->sv_path, orig_sv_path, svp_head->sv_pathlen); + origsvp = copy_svp(svp); nfs_rw_exit(&svp->sv_lock); /* @@ -2162,21 +2490,13 @@ nfs4rootvp(vnode_t **rtvpp, vfs_t *vfsp, struct servinfo4 *svp_head, break; /* - * replace current sv_path with orig sv_path -- just in - * case it changed due to embedded symlinks. + * For some reason, the mount compound failed. Before + * retrying, we need to restore original conditions. */ - (void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0); - if (orig_sv_pathlen != svp->sv_pathlen) { - kmem_free(svp->sv_path, svp->sv_pathlen); - svp->sv_path = kmem_alloc(orig_sv_pathlen, - KM_SLEEP); - svp->sv_pathlen = orig_sv_pathlen; - } - bcopy(orig_sv_path, svp->sv_path, orig_sv_pathlen); - nfs_rw_exit(&svp->sv_lock); + svp = restore_svp(mi, svp, origsvp); + svp_head = svp; } while (num_retry-- > 0); - error = e.error ? e.error : geterrno4(e.stat); if (error) { nfs_cmn_err(error, CE_WARN, @@ -2215,8 +2535,6 @@ nfs4rootvp(vnode_t **rtvpp, vfs_t *vfsp, struct servinfo4 *svp_head, firstsvp = svp; } - kmem_free(orig_sv_path, orig_sv_pathlen); - if (firstsvp == NULL) { if (error == 0) error = ENOENT; @@ -2286,6 +2604,19 @@ nfs4rootvp(vnode_t **rtvpp, vfs_t *vfsp, struct servinfo4 *svp_head, mi->mi_flags &= ~MI4_MOUNTING; mutex_exit(&mi->mi_lock); + /* Update VFS with new server and path info */ + if ((strcmp(svp->sv_hostname, origsvp->sv_hostname) != 0) || + (strcmp(svp->sv_path, origsvp->sv_path) != 0)) { + len = svp->sv_hostnamelen + svp->sv_pathlen; + resource = kmem_zalloc(len, KM_SLEEP); + (void) strcat(resource, svp->sv_hostname); + (void) strcat(resource, ":"); + (void) strcat(resource, svp->sv_path); + vfs_setresource(vfsp, resource); + kmem_free(resource, len); + } + + sv4_free(origsvp); *rtvpp = rtvp; if (lcr != NULL) crfree(lcr); @@ -2321,6 +2652,9 @@ bad: */ MI4_RELE(mi); + if (origsvp != NULL) + sv4_free(origsvp); + *rtvpp = NULL; return (error); } @@ -2336,7 +2670,6 @@ nfs4_unmount(vfs_t *vfsp, int flag, cred_t *cr) int removed; bool_t must_unlock; - bool_t must_rele; nfs4_ephemeral_tree_t *eph_tree; @@ -2388,7 +2721,7 @@ nfs4_unmount(vfs_t *vfsp, int flag, cred_t *cr) * again when needed. */ if (nfs4_ephemeral_umount(mi, flag, cr, - &must_unlock, &must_rele, &eph_tree)) { + &must_unlock, &eph_tree)) { ASSERT(must_unlock == FALSE); mutex_enter(&mi->mi_async_lock); mi->mi_max_threads = omax; @@ -2402,8 +2735,7 @@ nfs4_unmount(vfs_t *vfsp, int flag, cred_t *cr) * then the file system is busy and can't be unmounted. */ if (check_rtable4(vfsp)) { - nfs4_ephemeral_umount_unlock(&must_unlock, &must_rele, - &eph_tree); + nfs4_ephemeral_umount_unlock(&must_unlock, &eph_tree); mutex_enter(&mi->mi_async_lock); mi->mi_max_threads = omax; @@ -2416,8 +2748,7 @@ nfs4_unmount(vfs_t *vfsp, int flag, cred_t *cr) * The unmount can't fail from now on, so record any * ephemeral changes. */ - nfs4_ephemeral_umount_activate(mi, &must_unlock, - &must_rele, &eph_tree); + nfs4_ephemeral_umount_activate(mi, &must_unlock, &eph_tree); /* * There are no active files that could require over-the-wire @@ -2982,7 +3313,7 @@ recov_retry: */ if (FAILOVER_MOUNT4(mi) && nfs4_try_failover(n4ep)) { (void) nfs4_start_recovery(n4ep, mi, NULL, - NULL, NULL, NULL, OP_SETCLIENTID, NULL); + NULL, NULL, NULL, OP_SETCLIENTID, NULL, NULL, NULL); /* * Don't retry here, just return and let * recovery take over. @@ -3534,7 +3865,12 @@ new_nfs4_server(struct servinfo4 *svp, cred_t *cr) } un_curtime; verifier4 un_verifier; } nfs4clientid_verifier; - char id_val[] = "Solaris: %s, NFSv4 kernel client"; + /* + * We change this ID string carefully and with the Solaris + * NFS server behaviour in mind. "+referrals" indicates + * a client that can handle an NFSv4 referral. + */ + char id_val[] = "Solaris: %s, NFSv4 kernel client +referrals"; int len; np = kmem_zalloc(sizeof (struct nfs4_server), KM_SLEEP); @@ -3946,7 +4282,6 @@ nfs4_free_mount(vfs_t *vfsp, int flag, cred_t *cr) int removed; bool_t must_unlock; - bool_t must_rele; nfs4_ephemeral_tree_t *eph_tree; /* @@ -4020,9 +4355,9 @@ nfs4_free_mount(vfs_t *vfsp, int flag, cred_t *cr) * directory tree, we are okay. */ if (!nfs4_ephemeral_umount(mi, flag, cr, - &must_unlock, &must_rele, &eph_tree)) + &must_unlock, &eph_tree)) nfs4_ephemeral_umount_activate(mi, &must_unlock, - &must_rele, &eph_tree); + &eph_tree); /* * The original purge of the dnlc via 'dounmount' @@ -4058,3 +4393,81 @@ nfs4_free_mount(vfs_t *vfsp, int flag, cred_t *cr) if (removed) zone_rele(mi->mi_zone); } + +/* Referral related sub-routines */ + +/* Freeup knetconfig */ +static void +free_knconf_contents(struct knetconfig *k) +{ + if (k == NULL) + return; + if (k->knc_protofmly) + kmem_free(k->knc_protofmly, KNC_STRSIZE); + if (k->knc_proto) + kmem_free(k->knc_proto, KNC_STRSIZE); +} + +/* + * This updates newpath variable with exact name component from the + * path which gave us a NFS4ERR_MOVED error. + * If the path is /rp/aaa/bbb and nth value is 1, aaa is returned. + */ +static char * +extract_referral_point(const char *svp, int nth) +{ + int num_slashes = 0; + const char *p; + char *newpath = NULL; + int i = 0; + + newpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP); + for (p = svp; *p; p++) { + if (*p == '/') + num_slashes++; + if (num_slashes == nth + 1) { + p++; + while (*p != '/') { + if (*p == '\0') + break; + newpath[i] = *p; + i++; + p++; + } + newpath[i++] = '\0'; + break; + } + } + return (newpath); +} + +/* + * This sets up a new path in sv_path to do a lookup of the referral point. + * If the path is /rp/aaa/bbb and the referral point is aaa, + * this updates /rp/aaa. This path will be used to get referral + * location. + */ +static void +setup_newsvpath(servinfo4_t *svp, int nth) +{ + int num_slashes = 0, pathlen, i = 0; + char *newpath, *p; + + newpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP); + for (p = svp->sv_path; *p; p++) { + newpath[i] = *p; + if (*p == '/') + num_slashes++; + if (num_slashes == nth + 1) { + newpath[i] = '\0'; + pathlen = strlen(newpath) + 1; + kmem_free(svp->sv_path, svp->sv_pathlen); + svp->sv_path = kmem_alloc(pathlen, KM_SLEEP); + svp->sv_pathlen = pathlen; + bcopy(newpath, svp->sv_path, pathlen); + break; + } + i++; + } + kmem_free(newpath, MAXPATHLEN); +} diff --git a/usr/src/uts/common/fs/nfs/nfs4_vnops.c b/usr/src/uts/common/fs/nfs/nfs4_vnops.c index 5e79c7138f..49d2359d78 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_vnops.c +++ b/usr/src/uts/common/fs/nfs/nfs4_vnops.c @@ -90,6 +90,7 @@ #include <sys/ddi.h> #include <sys/int_fmtio.h> +#include <sys/fs/autofs.h> typedef struct { nfs4_ga_res_t *di_garp; @@ -164,7 +165,6 @@ static void nfs4_register_lock_locally(vnode_t *, struct flock64 *, int, static int nfs4_lockrelease(vnode_t *, int, offset_t, cred_t *); static int nfs4_block_and_wait(clock_t *, rnode4_t *); static cred_t *state_to_cred(nfs4_open_stream_t *); -static int vtoname(vnode_t *, char *, ssize_t); static void denied_to_flk(LOCK4denied *, flock64_t *, LOCKT4args *); static pid_t lo_to_pid(lock_owner4 *); static void nfs4_reinstitute_local_lock_state(vnode_t *, flock64_t *, @@ -1183,7 +1183,7 @@ recov_retry: abort = nfs4_start_recovery(&e, VTOMI4(dvp), dvp, vpi, NULL, lost_rqst.lr_op == OP_OPEN ? - &lost_rqst : NULL, OP_OPEN, bsep); + &lost_rqst : NULL, OP_OPEN, bsep, NULL, NULL); if (bsep) kmem_free(bsep, sizeof (*bsep)); @@ -1897,7 +1897,7 @@ top: abort = nfs4_start_recovery(ep, VTOMI4(vp), vp, NULL, NULL, lost_rqst.lr_op == OP_OPEN ? - &lost_rqst : NULL, OP_OPEN, NULL); + &lost_rqst : NULL, OP_OPEN, NULL, NULL, NULL); nfs4args_copen_free(open_args); goto bailout; } @@ -1936,7 +1936,7 @@ top: abort = nfs4_start_recovery(ep, VTOMI4(vp), vp, NULL, NULL, lost_rqst.lr_op == OP_OPEN ? &lost_rqst : - NULL, OP_OPEN, bsep); + NULL, OP_OPEN, bsep, NULL, NULL); nfs4args_copen_free(open_args); (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); @@ -1995,7 +1995,7 @@ top: case NFS4ERR_FHEXPIRED: /* recover filehandle and retry */ abort = nfs4_start_recovery(ep, - mi, vp, NULL, NULL, NULL, OP_OPEN, NULL); + mi, vp, NULL, NULL, NULL, OP_OPEN, NULL, NULL, NULL); nfs4args_copen_free(open_args); (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); nfs4_end_open_seqid_sync(oop); @@ -2511,7 +2511,7 @@ nfs4close_otw(rnode4_t *rp, cred_t *cred_otw, nfs4_open_owner_t *oop, abort = nfs4_start_recovery(ep, VTOMI4(vp), vp, NULL, NULL, (close_type != CLOSE_RESEND && lost_rqst.lr_op == OP_CLOSE) ? &lost_rqst : NULL, - OP_CLOSE, bsep); + OP_CLOSE, bsep, NULL, NULL); /* drop open seq sync, and let the calling function regrab it */ nfs4_end_open_seqid_sync(oop); @@ -3280,7 +3280,7 @@ recov_retry: abort = nfs4_start_recovery(&e, VTOMI4(vp), vp, NULL, &wargs->stateid, - NULL, OP_WRITE, NULL); + NULL, OP_WRITE, NULL, NULL, NULL); if (!e.error) { e.error = geterrno4(res.status); (void) xdr_free(xdr_COMPOUND4res_clnt, @@ -3540,7 +3540,7 @@ recov_retry: "nfs4read: initiating recovery\n")); abort = nfs4_start_recovery(&e, mi, vp, NULL, &rargs->stateid, - NULL, OP_READ, NULL); + NULL, OP_READ, NULL, NULL, NULL); nfs4_end_fop(mi, vp, NULL, OH_READ, &recov_state, needrecov); /* @@ -3987,7 +3987,7 @@ recov_retry: abort = nfs4_start_recovery(&e, VTOMI4(vp), vp, NULL, NULL, NULL, - OP_SETATTR, NULL); + OP_SETATTR, NULL, NULL, NULL); nfs4_end_op(VTOMI4(vp), vp, NULL, &recov_state, needrecov); /* @@ -4379,7 +4379,7 @@ recov_retry: "nfs4_access: initiating recovery\n")); if (nfs4_start_recovery(&e, VTOMI4(vp), vp, NULL, NULL, - NULL, OP_ACCESS, NULL) == FALSE) { + NULL, OP_ACCESS, NULL, NULL, NULL) == FALSE) { nfs4_end_fop(VTOMI4(vp), vp, NULL, OH_ACCESS, &recov_state, needrecov); if (!e.error) @@ -4536,7 +4536,7 @@ recov_retry: "nfs4_readlink: initiating recovery\n")); if (nfs4_start_recovery(&e, VTOMI4(vp), vp, NULL, NULL, - NULL, OP_READLINK, NULL) == FALSE) { + NULL, OP_READLINK, NULL, NULL, NULL) == FALSE) { if (!e.error) (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); @@ -4897,7 +4897,7 @@ recov_retry_remove: if (nfs4_needs_recovery(&e, FALSE, unldvp->v_vfsp)) { if (nfs4_start_recovery(&e, VTOMI4(unldvp), unldvp, NULL, - NULL, NULL, OP_REMOVE, NULL) == FALSE) { + NULL, NULL, OP_REMOVE, NULL, NULL, NULL) == FALSE) { if (!e.error) (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); @@ -5316,6 +5316,17 @@ recov_retry: rfs4call(VTOMI4(dvp), &args, &res, cr, &doqueue, 0, &e); + if (!isdotdot && res.status == NFS4ERR_MOVED) { + e.error = nfs4_setup_referral(dvp, nm, vpp, cr); + if (e.error != 0 && *vpp != NULL) + VN_RELE(*vpp); + nfs4_end_fop(mi, dvp, NULL, OH_LOOKUP, + &recov_state, FALSE); + (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); + kmem_free(argop, argoplist_size); + return (e.error); + } + if (nfs4_needs_recovery(&e, FALSE, dvp->v_vfsp)) { /* * For WRONGSEC of a non-dotdot case, send secinfo directly @@ -5343,7 +5354,7 @@ recov_retry: } if (nfs4_start_recovery(&e, mi, dvp, NULL, NULL, NULL, - OP_LOOKUP, NULL) == FALSE) { + OP_LOOKUP, NULL, NULL, NULL) == FALSE) { nfs4_end_fop(mi, dvp, NULL, OH_LOOKUP, &recov_state, TRUE); @@ -5743,6 +5754,17 @@ recov_retry: rfs4call(VTOMI4(dvp), &args, &res, cr, &doqueue, 0, &e); + if (!isdotdot && res.status == NFS4ERR_MOVED) { + e.error = nfs4_setup_referral(dvp, nm, vpp, cr); + if (e.error != 0 && *vpp != NULL) + VN_RELE(*vpp); + nfs4_end_fop(mi, dvp, NULL, OH_LOOKUP, + &recov_state, FALSE); + (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); + kmem_free(argop, argoplist_size); + return (e.error); + } + if (nfs4_needs_recovery(&e, FALSE, dvp->v_vfsp)) { /* * For WRONGSEC of a non-dotdot case, send secinfo directly @@ -5768,7 +5790,7 @@ recov_retry: } if (nfs4_start_recovery(&e, mi, dvp, NULL, NULL, NULL, - OP_LOOKUP, NULL) == FALSE) { + OP_LOOKUP, NULL, NULL, NULL) == FALSE) { nfs4_end_fop(mi, dvp, NULL, OH_LOOKUP, &recov_state, TRUE); @@ -6374,7 +6396,7 @@ recov_retry: abort = nfs4_start_recovery(&e, VTOMI4(dvp), dvp, NULL, NULL, NULL, - OP_OPENATTR, NULL); + OP_OPENATTR, NULL, NULL, NULL); nfs4_end_op(VTOMI4(dvp), dvp, NULL, &recov_state, needrecov); if (!e.error) { e.error = geterrno4(res.status); @@ -6986,7 +7008,7 @@ recov_retry: if (needrecov) { if (nfs4_start_recovery(&e, mi, dvp, NULL, NULL, NULL, - OP_CREATE, NULL) == FALSE) { + OP_CREATE, NULL, NULL, NULL) == FALSE) { nfs4_end_op(mi, dvp, NULL, &recov_state, needrecov); need_end_op = FALSE; @@ -7345,7 +7367,7 @@ recov_retry: if (needrecov) { if (nfs4_start_recovery(&e, VTOMI4(dvp), dvp, - NULL, NULL, NULL, OP_REMOVE, NULL) == FALSE) { + NULL, NULL, NULL, OP_REMOVE, NULL, NULL, NULL) == FALSE) { if (!e.error) (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); @@ -7519,7 +7541,7 @@ recov_retry: bool_t abort; abort = nfs4_start_recovery(&e, VTOMI4(svp), svp, tdvp, - NULL, NULL, OP_LINK, NULL); + NULL, NULL, OP_LINK, NULL, NULL, NULL); if (abort == FALSE) { nfs4_end_op(VTOMI4(svp), svp, tdvp, &recov_state, needrecov); @@ -8187,7 +8209,7 @@ recov_retry: if (needrecov) { if (nfs4_start_recovery(&e, mi, odvp, ndvp, NULL, NULL, - OP_RENAME, NULL) == FALSE) { + OP_RENAME, NULL, NULL, NULL) == FALSE) { nfs4_end_op(mi, odvp, ndvp, &recov_state, needrecov); if (!e.error) (void) xdr_free(xdr_COMPOUND4res_clnt, @@ -8444,7 +8466,7 @@ recov_retry: bool_t abort; abort = nfs4_start_recovery(&e, mi, odvp, ndvp, NULL, NULL, - OP_RENAME, NULL); + OP_RENAME, NULL, NULL, NULL); if (abort == FALSE) { nfs4_end_fop(mi, odvp, ndvp, OH_VFH_RENAME, &recov_state, needrecov); @@ -8722,7 +8744,7 @@ recov_retry: if (needrecov) { if (nfs4_start_recovery(&e, VTOMI4(dvp), dvp, NULL, NULL, - NULL, OP_REMOVE, NULL) == FALSE) { + NULL, OP_REMOVE, NULL, NULL, NULL) == FALSE) { if (!e.error) (void) xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res); @@ -9319,7 +9341,7 @@ recov_retry: "nfs4readdir: initiating recovery.\n")); abort = nfs4_start_recovery(&e, VTOMI4(vp), vp, NULL, NULL, - NULL, OP_READDIR, NULL); + NULL, OP_READDIR, NULL, NULL, NULL); if (abort == FALSE) { nfs4_end_fop(VTOMI4(vp), vp, NULL, OH_READDIR, &recov_state, needrecov); @@ -11756,7 +11778,7 @@ recov_retry: if (needrecov) { if (nfs4_start_recovery(&e, VTOMI4(vp), vp, NULL, NULL, - NULL, OP_COMMIT, NULL) == FALSE) { + NULL, OP_COMMIT, NULL, NULL, NULL) == FALSE) { nfs4_end_fop(VTOMI4(vp), vp, NULL, OH_COMMIT, &recov_state, needrecov); if (!e.error) @@ -12318,6 +12340,12 @@ nfs4_getsecattr(vnode_t *vp, vsecattr_t *vsecattr, int flag, cred_t *cr, if (error) /* EINVAL */ return (error); + /* + * If this is a referral stub, don't try to go OTW for an ACL + */ + if (RP_ISSTUB_REFERRAL(VTOR4(vp))) + return (fs_fab_acl(vp, vsecattr, flag, cr, ct)); + if (mi->mi_flags & MI4_ACL) { /* * Check if the data is cached and the cache is valid. If it @@ -12752,8 +12780,8 @@ recov_retry_confirm: vp, 0, args.ctag, open_confirm_args->seqid); - abort = nfs4_start_recovery(ep, VTOMI4(vp), vp, - NULL, NULL, NULL, OP_OPEN_CONFIRM, bsep); + abort = nfs4_start_recovery(ep, VTOMI4(vp), vp, NULL, + NULL, NULL, OP_OPEN_CONFIRM, bsep, NULL, NULL); if (bsep) { kmem_free(bsep, sizeof (*bsep)); if (num_bseqid_retryp && @@ -13686,7 +13714,7 @@ nfs4frlock_recovery(int needrecov, nfs4_error_t *ep, abort = nfs4_start_recovery(ep, VTOMI4(vp), vp, NULL, NULL, (lost_rqstp && (lost_rqstp->lr_op == OP_LOCK || lost_rqstp->lr_op == OP_LOCKU)) ? lost_rqstp : - NULL, op, bsep); + NULL, op, bsep, NULL, NULL); if (bsep) kmem_free(bsep, sizeof (*bsep)); @@ -14215,7 +14243,7 @@ recov_retry: VTOMI4(vp), vp, NULL, NULL, (lost_rqst.lr_op == OP_LOCK || lost_rqst.lr_op == OP_LOCKU) ? - &lost_rqst : NULL, OP_LOCKU, NULL); + &lost_rqst : NULL, OP_LOCKU, NULL, NULL, NULL); lock_owner_rele(lop); lop = NULL; } @@ -15303,7 +15331,7 @@ recov_retry: have_sync_lock = 0; (void) nfs4_start_recovery(ep, mi, vp, NULL, NULL, lost_rqst.lr_op == OP_CLOSE ? - &lost_rqst : NULL, OP_CLOSE, NULL); + &lost_rqst : NULL, OP_CLOSE, NULL, NULL, NULL); close_failed = 1; force_close = 0; goto close_cleanup; @@ -15387,7 +15415,7 @@ recov_retry: abort = nfs4_start_recovery(ep, mi, vp, NULL, NULL, new_lost_rqst.lr_op == OP_OPEN_DOWNGRADE ? &new_lost_rqst : NULL, OP_OPEN_DOWNGRADE, - bsep); + bsep, NULL, NULL); if (odg_cred_otw) crfree(odg_cred_otw); if (bsep) @@ -15976,5 +16004,5 @@ push_reinstate(vnode_t *vp, int cmd, flock64_t *flk, cred_t *cr, (void) nfs4_start_recovery(&e, VTOMI4(vp), vp, NULL, NULL, (req.lr_op == OP_LOCK || req.lr_op == OP_LOCKU) ? &req : NULL, flk->l_type == F_UNLCK ? OP_LOCKU : OP_LOCK, - NULL); + NULL, NULL, NULL); } diff --git a/usr/src/uts/common/fs/nfs/nfs4_xdr.c b/usr/src/uts/common/fs/nfs/nfs4_xdr.c index 1b3d1462a6..e2e14cff8a 100644 --- a/usr/src/uts/common/fs/nfs/nfs4_xdr.c +++ b/usr/src/uts/common/fs/nfs/nfs4_xdr.c @@ -42,7 +42,16 @@ #include <nfs/nfs4.h> #include <nfs/nfs4_clnt.h> #include <sys/sdt.h> +#include <sys/mkdev.h> #include <rpc/rpc_rdma.h> +#include <rpc/xdr.h> + +#define xdr_dev_t xdr_u_int + +extern bool_t xdr_netbuf(XDR *, struct netbuf *); +extern bool_t xdr_vector(XDR *, char *, const uint_t, const uint_t, + const xdrproc_t); +bool_t xdr_knetconfig(XDR *, struct knetconfig *); bool_t xdr_bitmap4(XDR *xdrs, bitmap4 *objp) @@ -146,6 +155,143 @@ xdr_utf8string(XDR *xdrs, utf8string *objp) } /* + * used by NFSv4 referrals to get info needed for NFSv4 referral mount. + */ +bool_t +xdr_nfs_fsl_info(XDR *xdrs, struct nfs_fsl_info *objp) +{ + + if (!xdr_u_int(xdrs, &objp->netbuf_len)) + return (FALSE); + if (!xdr_u_int(xdrs, &objp->netnm_len)) + return (FALSE); + if (!xdr_u_int(xdrs, &objp->knconf_len)) + return (FALSE); + +#if defined(_LP64) + /* + * The object can come from a 32-bit binary; nfsmapid. + * To be safe we double the size of the knetconfig to + * allow some buffering for decoding. + */ + if (xdrs->x_op == XDR_DECODE) + objp->knconf_len += sizeof (struct knetconfig); +#endif + + if (!xdr_string(xdrs, &objp->netname, ~0)) + return (FALSE); + if (!xdr_pointer(xdrs, (char **)&objp->addr, objp->netbuf_len, + (xdrproc_t)xdr_netbuf)) + return (FALSE); + if (!xdr_pointer(xdrs, (char **)&objp->knconf, + objp->knconf_len, (xdrproc_t)xdr_knetconfig)) + return (FALSE); + return (TRUE); +} + +bool_t +xdr_knetconfig(XDR *xdrs, struct knetconfig *objp) +{ + rpc_inline_t *buf; + u_longlong_t dev64; +#if !defined(_LP64) + uint32_t major, minor; +#endif + int i; + + if (!xdr_u_int(xdrs, &objp->knc_semantics)) + return (FALSE); + if (xdrs->x_op == XDR_DECODE) { + objp->knc_protofmly = (((char *)objp) + + sizeof (struct knetconfig)); + objp->knc_proto = objp->knc_protofmly + KNC_STRSIZE; + } + if (!xdr_opaque(xdrs, objp->knc_protofmly, KNC_STRSIZE)) + return (FALSE); + if (!xdr_opaque(xdrs, objp->knc_proto, KNC_STRSIZE)) + return (FALSE); + + /* + * For interoperability between 32-bit daemon and 64-bit kernel, + * we always treat dev_t as 64-bit number and do the expanding + * or compression of dev_t as needed. + * We have to hand craft the conversion since there is no available + * function in ddi.c. Besides ddi.c is available only in the kernel + * and we want to keep both user and kernel of xdr_knetconfig() the + * same for consistency. + */ + if (xdrs->x_op == XDR_ENCODE) { +#if defined(_LP64) + dev64 = objp->knc_rdev; +#else + major = (objp->knc_rdev >> NBITSMINOR32) & MAXMAJ32; + minor = objp->knc_rdev & MAXMIN32; + dev64 = (((unsigned long long)major) << NBITSMINOR64) | minor; +#endif + if (!xdr_u_longlong_t(xdrs, &dev64)) + return (FALSE); + } + if (xdrs->x_op == XDR_DECODE) { +#if defined(_LP64) + if (!xdr_u_longlong_t(xdrs, (u_longlong_t *)&objp->knc_rdev)) + return (FALSE); +#else + if (!xdr_u_longlong_t(xdrs, &dev64)) + return (FALSE); + + major = (dev64 >> NBITSMINOR64) & L_MAXMAJ32; + minor = dev64 & L_MAXMIN32; + objp->knc_rdev = (major << L_BITSMINOR32) | minor; +#endif + } + + if (xdrs->x_op == XDR_ENCODE) { + buf = XDR_INLINE(xdrs, (8) * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8, + sizeof (uint_t), (xdrproc_t)xdr_u_int)) + return (FALSE); + } else { + uint_t *genp; + + for (i = 0, genp = objp->knc_unused; + i < 8; i++) { +#if defined(_LP64) || defined(_KERNEL) + IXDR_PUT_U_INT32(buf, *genp++); +#else + IXDR_PUT_U_LONG(buf, *genp++); +#endif + } + } + return (TRUE); + } else if (xdrs->x_op == XDR_DECODE) { + buf = XDR_INLINE(xdrs, (8) * BYTES_PER_XDR_UNIT); + if (buf == NULL) { + if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8, + sizeof (uint_t), (xdrproc_t)xdr_u_int)) + return (FALSE); + } else { + uint_t *genp; + + for (i = 0, genp = objp->knc_unused; + i < 8; i++) { +#if defined(_LP64) || defined(_KERNEL) + *genp++ = IXDR_GET_U_INT32(buf); +#else + *genp++ = IXDR_GET_U_LONG(buf); +#endif + } + } + return (TRUE); + } + + if (!xdr_vector(xdrs, (char *)objp->knc_unused, 8, + sizeof (uint_t), (xdrproc_t)xdr_u_int)) + return (FALSE); + return (TRUE); +} + +/* * XDR_INLINE decode a filehandle. */ bool_t @@ -492,8 +638,12 @@ xdr_nfs_fh4(XDR *xdrs, nfs_fh4 *objp) static bool_t xdr_fs_location4(XDR *xdrs, fs_location4 *objp) { + if (xdrs->x_op == XDR_DECODE) { + objp->server_val = NULL; + objp->rootpath.pathname4_val = NULL; + } if (!xdr_array(xdrs, (char **)&objp->server_val, - (uint_t *)&objp->server_len, NFS4_FS_LOCATIONS_LIMIT, + (uint_t *)&objp->server_len, NFS4_MAX_UTF8STRING, sizeof (utf8string), (xdrproc_t)xdr_utf8string)) return (FALSE); return (xdr_array(xdrs, (char **)&objp->rootpath.pathname4_val, @@ -560,6 +710,11 @@ xdr_fattr4_acl(XDR *xdrs, fattr4_acl *objp) bool_t xdr_fattr4_fs_locations(XDR *xdrs, fattr4_fs_locations *objp) { + if (xdrs->x_op == XDR_DECODE) { + objp->fs_root.pathname4_len = 0; + objp->fs_root.pathname4_val = NULL; + objp->locations_val = NULL; + } if (!xdr_array(xdrs, (char **)&objp->fs_root.pathname4_val, (uint_t *)&objp->fs_root.pathname4_len, NFS4_MAX_PATHNAME4, @@ -930,7 +1085,9 @@ xdr_ga_fattr_res(XDR *xdrs, struct nfs4_ga_res *garp, bitmap4 resbmap, FATTR4_HOMOGENEOUS_MASK)) { if (resbmap & FATTR4_FS_LOCATIONS_MASK) { - ASSERT(0); + if (!xdr_fattr4_fs_locations(xdrs, + &gesp->n4g_fslocations)) + return (FALSE); } if (resbmap & FATTR4_HIDDEN_MASK) { ASSERT(0); @@ -2253,7 +2410,9 @@ noentries: static bool_t xdr_ga_res(XDR *xdrs, GETATTR4res *objp, GETATTR4args *aobjp) { +#ifdef INLINE uint32_t *ptr; +#endif bitmap4 resbmap; uint32_t attrlen; @@ -2307,11 +2466,13 @@ xdr_ga_res(XDR *xdrs, GETATTR4res *objp, GETATTR4args *aobjp) } /* Check to see if the attrs can be inlined and go for it if so */ +#ifdef INLINE if (!(resbmap & FATTR4_ACL_MASK) && (ptr = (uint32_t *)XDR_INLINE(xdrs, attrlen)) != NULL) return (xdr_ga_fattr_res_inline(ptr, &objp->ga_res, resbmap, aobjp->attr_request, aobjp->mi, NULL)); else +#endif return (xdr_ga_fattr_res(xdrs, &objp->ga_res, resbmap, aobjp->attr_request, aobjp->mi, NULL)); } @@ -4300,6 +4461,7 @@ xdr_nfs_resop4_free(XDR *xdrs, nfs_resop4 **arrayp, int len, int decode_len) { int i; nfs_resop4 *array = *arrayp; + nfs4_ga_res_t *gr; /* * Optimized XDR_FREE only results array @@ -4319,10 +4481,15 @@ xdr_nfs_resop4_free(XDR *xdrs, nfs_resop4 **arrayp, int len, int decode_len) case OP_GETATTR: if (array[i].nfs_resop4_u.opgetattr.status != NFS4_OK) continue; - if (array[i].nfs_resop4_u.opgetattr.ga_res.n4g_ext_res) - kmem_free(array[i].nfs_resop4_u.opgetattr. - ga_res.n4g_ext_res, + + gr = &array[i].nfs_resop4_u.opgetattr.ga_res; + if (gr->n4g_ext_res) { + if (gr->n4g_resbmap & FATTR4_FS_LOCATIONS_MASK) + (void) xdr_fattr4_fs_locations(xdrs, + &gr->n4g_ext_res->n4g_fslocations); + kmem_free(gr->n4g_ext_res, sizeof (struct nfs4_ga_ext_res)); + } continue; case OP_GETFH: if (array[i].nfs_resop4_u.opgetfh.status != NFS4_OK) diff --git a/usr/src/uts/common/fs/nfs/nfs_srv.c b/usr/src/uts/common/fs/nfs/nfs_srv.c index 4fb64c62b1..4d98017403 100644 --- a/usr/src/uts/common/fs/nfs/nfs_srv.c +++ b/usr/src/uts/common/fs/nfs/nfs_srv.c @@ -117,6 +117,10 @@ rfs_getattr(fhandle_t *fhp, struct nfsattrstat *ns, struct exportinfo *exi, /* check for overflows */ if (!error) { + /* Lie about the object type for a referral */ + if (vn_is_nfs_reparse(vp, cr)) + va.va_type = VLNK; + acl_perm(vp, exi, &va, cr); error = vattr_to_nattr(&va, &ns->ns_attr); } @@ -489,6 +493,7 @@ rfs_readlink(fhandle_t *fhp, struct nfsrdlnres *rl, struct exportinfo *exi, struct vattr va; struct sockaddr *ca; char *name = NULL; + int is_referral = 0; vp = nfs_fhtovp(fhp, exi); if (vp == NULL) { @@ -515,11 +520,15 @@ rfs_readlink(fhandle_t *fhp, struct nfsrdlnres *rl, struct exportinfo *exi, return; } + /* We lied about the object type for a referral */ + if (vn_is_nfs_reparse(vp, cr)) + is_referral = 1; + /* * XNFS and RFC1094 require us to return ENXIO if argument * is not a link. BUGID 1138002. */ - if (vp->v_type != VLNK) { + if (vp->v_type != VLNK && !is_referral) { VN_RELE(vp); rl->rl_data = NULL; rl->rl_status = NFSERR_NXIO; @@ -531,27 +540,52 @@ rfs_readlink(fhandle_t *fhp, struct nfsrdlnres *rl, struct exportinfo *exi, */ rl->rl_data = kmem_alloc(NFS_MAXPATHLEN, KM_SLEEP); - /* - * Set up io vector to read sym link data - */ - iov.iov_base = rl->rl_data; - iov.iov_len = NFS_MAXPATHLEN; - uio.uio_iov = &iov; - uio.uio_iovcnt = 1; - uio.uio_segflg = UIO_SYSSPACE; - uio.uio_extflg = UIO_COPY_CACHED; - uio.uio_loffset = (offset_t)0; - uio.uio_resid = NFS_MAXPATHLEN; + if (is_referral) { + char *s; + size_t strsz; + + /* Get an artificial symlink based on a referral */ + s = build_symlink(vp, cr, &strsz); + global_svstat_ptr[2][NFS_REFERLINKS].value.ui64++; + DTRACE_PROBE2(nfs2serv__func__referral__reflink, + vnode_t *, vp, char *, s); + if (s == NULL) + error = EINVAL; + else { + error = 0; + (void) strlcpy(rl->rl_data, s, NFS_MAXPATHLEN); + rl->rl_count = (uint32_t)MIN(strsz, NFS_MAXPATHLEN); + kmem_free(s, strsz); + } - /* - * Do the readlink. - */ - error = VOP_READLINK(vp, &uio, cr, NULL); + } else { - VN_RELE(vp); + /* + * Set up io vector to read sym link data + */ + iov.iov_base = rl->rl_data; + iov.iov_len = NFS_MAXPATHLEN; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_extflg = UIO_COPY_CACHED; + uio.uio_loffset = (offset_t)0; + uio.uio_resid = NFS_MAXPATHLEN; + + /* + * Do the readlink. + */ + error = VOP_READLINK(vp, &uio, cr, NULL); + + rl->rl_count = (uint32_t)(NFS_MAXPATHLEN - uio.uio_resid); - rl->rl_count = (uint32_t)(NFS_MAXPATHLEN - uio.uio_resid); - rl->rl_data[rl->rl_count] = '\0'; + if (!error) + rl->rl_data[rl->rl_count] = '\0'; + + } + + + VN_RELE(vp); ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf; name = nfscmd_convname(ca, exi, rl->rl_data, diff --git a/usr/src/uts/common/fs/nfs/nfs_stats.c b/usr/src/uts/common/fs/nfs/nfs_stats.c index b125a06d6c..baaf47a82a 100644 --- a/usr/src/uts/common/fs/nfs/nfs_stats.c +++ b/usr/src/uts/common/fs/nfs/nfs_stats.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <sys/kstat.h> #include <sys/zone.h> @@ -87,6 +85,8 @@ nfsstat_zone_fini_common(zoneid_t zoneid, const char *module, int vers, static const kstat_named_t svstat_tmpl[] = { { "calls", KSTAT_DATA_UINT64 }, { "badcalls", KSTAT_DATA_UINT64 }, + { "referrals", KSTAT_DATA_UINT64 }, + { "referlinks", KSTAT_DATA_UINT64 }, }; /* Points to the global zone server kstat data for all nfs versions */ @@ -108,7 +108,7 @@ nfsstat_zone_init_server(zoneid_t zoneid, kstat_named_t *svstatp[]) for (vers = NFS_VERSION; vers <= NFS_V4; vers++) { svstatp[vers] = nfsstat_zone_init_common(zoneid, "nfs", vers, - "nfs_server", svstat_tmpl, sizeof (svstat_tmpl)); + "nfs_server", svstat_tmpl, sizeof (svstat_tmpl)); if (zoneid == GLOBAL_ZONEID) global_svstat_ptr[vers] = svstatp[vers]; } diff --git a/usr/src/uts/common/fs/vnode.c b/usr/src/uts/common/fs/vnode.c index a42259df93..2f7aa751ad 100644 --- a/usr/src/uts/common/fs/vnode.c +++ b/usr/src/uts/common/fs/vnode.c @@ -4441,3 +4441,32 @@ fs_reparse_mark(char *target, vattr_t *vap, xvattr_t *xvattr) return (0); } + +/* + * Function to check whether a symlink is a reparse point. + * Return B_TRUE if it is a reparse point, else return B_FALSE + */ +boolean_t +vn_is_reparse(vnode_t *vp, cred_t *cr, caller_context_t *ct) +{ + xvattr_t xvattr; + xoptattr_t *xoap; + + if ((vp->v_type != VLNK) || + !(vfs_has_feature(vp->v_vfsp, VFSFT_XVATTR))) + return (B_FALSE); + + xva_init(&xvattr); + xoap = xva_getxoptattr(&xvattr); + ASSERT(xoap); + XVA_SET_REQ(&xvattr, XAT_REPARSE); + + if (VOP_GETATTR(vp, &xvattr.xva_vattr, 0, cr, ct)) + return (B_FALSE); + + if ((!(xvattr.xva_vattr.va_mask & AT_XVATTR)) || + (!(XVA_ISSET_RTN(&xvattr, XAT_REPARSE)))) + return (B_FALSE); + + return (xoap->xoa_reparse ? B_TRUE : B_FALSE); +} diff --git a/usr/src/uts/common/nfs/export.h b/usr/src/uts/common/nfs/export.h index b66c00c389..21d6e9e4f6 100644 --- a/usr/src/uts/common/nfs/export.h +++ b/usr/src/uts/common/nfs/export.h @@ -295,6 +295,7 @@ struct charset_cache { /* Forward declarations */ struct exportinfo; struct exp_visible; +struct svc_req; /* * Treenodes are used to build tree representing every node which is part @@ -409,6 +410,7 @@ struct exportinfo { struct exp_visible *exi_visible; struct charset_cache *exi_charset; unsigned exi_volatile_dev:1; + unsigned exi_moved:1; #ifdef VOLATILE_FH_TEST uint32_t exi_volatile_id; struct ex_vol_rename *exi_vol_rename; @@ -512,6 +514,9 @@ extern void export_link(struct exportinfo *); extern int export_unlink(fsid_t *, fid_t *, vnode_t *, struct exportinfo **); extern vnode_t *untraverse(vnode_t *); +extern int vn_is_nfs_reparse(vnode_t *, cred_t *); +extern int client_is_downrev(struct svc_req *); +extern char *build_symlink(vnode_t *, cred_t *, size_t *); /* * Functions that handle the NFSv4 server namespace diff --git a/usr/src/uts/common/nfs/mount.h b/usr/src/uts/common/nfs/mount.h index 4b5c3adb9c..c2785ce73f 100644 --- a/usr/src/uts/common/nfs/mount.h +++ b/usr/src/uts/common/nfs/mount.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -31,8 +31,6 @@ #ifndef _NFS_MOUNT_H #define _NFS_MOUNT_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -157,11 +155,9 @@ struct nfs_args32 { #define NFSMNT_TRYRDMA 0x8000000 /* Try RDMA mount,no proto advised */ #define NFSMNT_DORDMA 0x10000000 /* Do an RDMA mount, regardless */ #define NFSMNT_MIRRORMOUNT 0x20000000 /* Is a mirrormount */ +#define NFSMNT_REFERRAL 0x40000000 /* Is a referral */ -/* - * This will have to change when we do referrals. - */ -#define NFSMNT_EPHEMERAL NFSMNT_MIRRORMOUNT +#define NFSMNT_EPHEMERAL (NFSMNT_MIRRORMOUNT | NFSMNT_REFERRAL) #ifdef __cplusplus } diff --git a/usr/src/uts/common/nfs/nfs.h b/usr/src/uts/common/nfs/nfs.h index a3aa522944..77e5a397c2 100644 --- a/usr/src/uts/common/nfs/nfs.h +++ b/usr/src/uts/common/nfs/nfs.h @@ -884,7 +884,8 @@ extern void rfs_srvrfini(void); #define NATIVEPATH 0x02 /* Native path, i.e., via mount protocol */ #define SECURITY_QUERY 0x04 /* Security query */ -enum nfs_svccounts {NFS_CALLS, NFS_BADCALLS}; /* index for svstat_ptr */ +/* index for svstat_ptr */ +enum nfs_svccounts {NFS_CALLS, NFS_BADCALLS, NFS_REFERRALS, NFS_REFERLINKS}; /* function defs for NFS kernel */ extern int nfs_waitfor_purge_complete(vnode_t *); diff --git a/usr/src/uts/common/nfs/nfs4.h b/usr/src/uts/common/nfs/nfs4.h index eccc885a2d..c72c238f7e 100644 --- a/usr/src/uts/common/nfs/nfs4.h +++ b/usr/src/uts/common/nfs/nfs4.h @@ -460,6 +460,7 @@ nvlist_t *rfs4_dss_paths, *rfs4_dss_oldpaths; * ss_remove - indicates that the rfs4_client_destroy function should * clean up stable storage file. * forced_expire - set if the sysadmin has used clear_locks for this client. + * no_referrals - set if the client is Solaris and pre-dates referrals * deleg_revoked - how many delegations have been revoked for this client? * * cp_confirmed - this refers to a confirmed client struct that has @@ -499,6 +500,17 @@ typedef struct rfs4_client { } rfs4_client_t; /* + * ClntIP struct - holds the diagnosis about whether the client + * cannot support referrals. Set to true for old Solaris clients. + */ + +typedef struct rfs4_clntip { + rfs4_dbe_t *ri_dbe; + struct sockaddr_storage ri_addr; + unsigned ri_no_referrals:1; +} rfs4_clntip_t; + +/* * The openowner contains the client supplied open_owner4 as well as * the matching sequence id and is used to track the client's usage of * the open_owner4. Note that a reply is saved here as well for @@ -775,6 +787,7 @@ extern void rfs4_copy_reply(nfs_resop4 *, nfs_resop4 *); extern rfs4_client_t *rfs4_findclient(nfs_client_id4 *, bool_t *, rfs4_client_t *); extern rfs4_client_t *rfs4_findclient_by_id(clientid4, bool_t); +extern rfs4_client_t *rfs4_findclient_by_addr(struct sockaddr *); extern void rfs4_client_rele(rfs4_client_t *); extern void rfs4_client_close(rfs4_client_t *); extern void rfs4_client_state_remove(rfs4_client_t *); @@ -783,6 +796,10 @@ extern void rfs4_update_lease(rfs4_client_t *); extern bool_t rfs4_lease_expired(rfs4_client_t *); extern nfsstat4 rfs4_check_clientid(clientid4 *, int); +/* rfs4_clntip_t handling */ +extern rfs4_clntip_t *rfs4_find_clntip(struct sockaddr *, bool_t *); +extern void rfs4_invalidate_clntip(struct sockaddr *); + /* rfs4_openowner_t handling */ extern rfs4_openowner_t *rfs4_findopenowner(open_owner4 *, bool_t *, seqid4); extern void rfs4_update_open_sequence(rfs4_openowner_t *); @@ -1095,6 +1112,7 @@ struct nfs4_svgetit_arg { /* rdattr_error */ nfsstat4 rdattr_error; /* used for per-entry status */ /* (if rdattr_err) */ + bool_t is_referral; /* because sometimes we tell lies */ bool_t mntdfid_set; fattr4_mounted_on_fileid mounted_on_fileid; @@ -1302,6 +1320,11 @@ extern void vs_acet_destroy(vsecattr_t *); extern void vs_ace4_destroy(vsecattr_t *); extern void vs_aent_destroy(vsecattr_t *); +extern int vn_find_nfs_record(vnode_t *, nvlist_t **, char **, char **); +extern int vn_is_nfs_reparse(vnode_t *, cred_t *); +extern fs_locations4 *fetch_referral(vnode_t *, cred_t *); +extern char *build_symlink(vnode_t *, cred_t *, size_t *); + extern int stateid4_cmp(stateid4 *, stateid4 *); extern vtype_t nf4_to_vt[]; diff --git a/usr/src/uts/common/nfs/nfs4_attr.h b/usr/src/uts/common/nfs/nfs4_attr.h index aa28ff9d40..44acf555b4 100644 --- a/usr/src/uts/common/nfs/nfs4_attr.h +++ b/usr/src/uts/common/nfs/nfs4_attr.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _NFS4_ATTR_H #define _NFS4_ATTR_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -436,6 +433,7 @@ typedef struct nfs4_ga_ext_res { * ACL4_SUPPORT_ALARM_ACL */ fattr4_aclsupport n4g_aclsupport; + fattr4_fs_locations n4g_fslocations; } nfs4_ga_ext_res_t; extern bitmap4 rfs4_supported_attrs; diff --git a/usr/src/uts/common/nfs/nfs4_clnt.h b/usr/src/uts/common/nfs/nfs4_clnt.h index e0d9852c78..8796b96e0b 100644 --- a/usr/src/uts/common/nfs/nfs4_clnt.h +++ b/usr/src/uts/common/nfs/nfs4_clnt.h @@ -46,6 +46,7 @@ #include <sys/avl.h> #include <sys/list.h> #include <rpc/auth.h> +#include <sys/door.h> #ifdef __cplusplus extern "C" { @@ -161,6 +162,8 @@ typedef struct nfs4_delmap_args { struct clstat4 { kstat_named_t calls; /* client requests */ kstat_named_t badcalls; /* rpc failures */ + kstat_named_t referrals; /* referrals */ + kstat_named_t referlinks; /* referrals as symlinks */ kstat_named_t clgets; /* client handle gets */ kstat_named_t cltoomany; /* client handle cache misses */ #ifdef DEBUG @@ -302,6 +305,7 @@ typedef enum nfs4_tag_type { TAG_FSINFO, TAG_GET_SYMLINK, TAG_GETATTR, + TAG_GETATTR_FSLOCATION, TAG_INACTIVE, TAG_LINK, TAG_LOCK, @@ -374,6 +378,8 @@ typedef enum nfs4_tag_type { {0x67657420, 0x736c6e6b, 0x20747874}}, \ {TAG_GETATTR, "getattr", \ {0x67657461, 0x74747220, 0x20202020}}, \ + {TAG_GETATTR_FSLOCATION, "getattr fslocation", \ + {0x67657461, 0x74747220, 0x66736c6f}}, \ {TAG_INACTIVE, "inactive", \ {0x696e6163, 0x74697665, 0x20202020}}, \ {TAG_LINK, "link", \ @@ -553,7 +559,8 @@ typedef struct nfs4_open_owner { /* * Static server information. - * These fields are read-only once they are initialized: + * These fields are read-only once they are initialized; sv_lock + * should be held as writer if they are changed during mount: * sv_addr * sv_dhsec * sv_hostname @@ -699,7 +706,8 @@ typedef enum { NR_DELAY, NR_LOST_LOCK, NR_LOST_STATE_RQST, - NR_STALE + NR_STALE, + NR_MOVED } nfs4_recov_t; /* @@ -709,6 +717,8 @@ typedef enum { #define NFS4_MSG_MAX 100 extern int nfs4_msg_max; +#define NFS4_REFERRAL_LOOP_MAX 20 + typedef enum { RE_BAD_SEQID, RE_BADHANDLE, @@ -729,7 +739,8 @@ typedef enum { RE_UNEXPECTED_ERRNO, RE_UNEXPECTED_STATUS, RE_WRONGSEC, - RE_LOST_STATE_BAD_OP + RE_LOST_STATE_BAD_OP, + RE_REFERRAL } nfs4_event_type_t; typedef enum { @@ -1033,6 +1044,10 @@ typedef struct mntinfo4 { uint_t mi_srvset_cnt; /* increment when changing the nfs4_server_t */ struct nfs4_server *mi_srv; /* backpointer to nfs4_server_t */ + /* + * Referral related info. + */ + int mi_vfs_referral_loop_cnt; } mntinfo4_t; /* @@ -1085,7 +1100,7 @@ typedef struct mntinfo4 { #define MI4_ACL 0x2000 /* MI4_MIRRORMOUNT is also defined in nfsstat.c */ #define MI4_MIRRORMOUNT 0x4000 -/* 0x8000 is available */ +#define MI4_REFERRAL 0x8000 /* 0x10000 is available */ #define MI4_NOPRINT 0x20000 #define MI4_DIRECTIO 0x40000 @@ -1103,11 +1118,7 @@ typedef struct mntinfo4 { #define MI4_ASYNC_MGR_STOP 0x40000000 #define MI4_TIMEDOUT 0x80000000 -/* - * Note that when we add referrals, then MI4_EPHEMERAL - * will be MI4_MIRRORMOUNT | MI4_REFERRAL. - */ -#define MI4_EPHEMERAL MI4_MIRRORMOUNT +#define MI4_EPHEMERAL (MI4_MIRRORMOUNT | MI4_REFERRAL) #define INTR4(vp) (VTOMI4(vp)->mi_flags & MI4_INT) @@ -1128,6 +1139,7 @@ typedef struct mntinfo4 { #define MI4R_SRV_REBOOT 0x20 /* server has rebooted */ #define MI4R_LOST_STATE 0x40 #define MI4R_BAD_SEQID 0x80 +#define MI4R_MOVED 0x100 #define MI4_HOLD(mi) { \ mi_hold(mi); \ @@ -1492,6 +1504,7 @@ extern void nfs4_write_error(vnode_t *, int, cred_t *); extern void nfs4_lockcompletion(vnode_t *, int); extern bool_t nfs4_map_lost_lock_conflict(vnode_t *); extern int vtodv(vnode_t *, vnode_t **, cred_t *, bool_t); +extern int vtoname(vnode_t *, char *, ssize_t); extern void nfs4open_confirm(vnode_t *, seqid4*, stateid4 *, cred_t *, bool_t, bool_t *, nfs4_open_owner_t *, bool_t, nfs4_error_t *, int *); @@ -1502,6 +1515,9 @@ extern void nfs4_free_args(struct nfs_args *); extern void mi_hold(mntinfo4_t *); extern void mi_rele(mntinfo4_t *); +extern vnode_t *find_referral_stubvp(vnode_t *, char *, cred_t *); +extern int nfs4_setup_referral(vnode_t *, char *, vnode_t **, cred_t *); + extern sec_data_t *copy_sec_data(sec_data_t *); extern gss_clntdata_t *copy_sec_data_gss(gss_clntdata_t *); @@ -1969,7 +1985,8 @@ extern int nfs4_needs_recovery(nfs4_error_t *, bool_t, vfs_t *); extern int nfs4_recov_marks_dead(nfsstat4); extern bool_t nfs4_start_recovery(nfs4_error_t *, struct mntinfo4 *, vnode_t *, vnode_t *, stateid4 *, - nfs4_lost_rqst_t *, nfs_opnum4, nfs4_bseqid_entry_t *); + nfs4_lost_rqst_t *, nfs_opnum4, nfs4_bseqid_entry_t *, + vnode_t *, char *); extern int nfs4_start_op(struct mntinfo4 *, vnode_t *, vnode_t *, nfs4_recov_state_t *); extern void nfs4_end_op(struct mntinfo4 *, vnode_t *, vnode_t *, @@ -1991,14 +2008,18 @@ extern char *nfs4_recov_action_to_str(nfs4_recov_t); * of whether or not the code in _unlock is to be ran. */ extern void nfs4_ephemeral_umount_activate(mntinfo4_t *, - bool_t *, bool_t *, nfs4_ephemeral_tree_t **); + bool_t *, nfs4_ephemeral_tree_t **); extern int nfs4_ephemeral_umount(mntinfo4_t *, int, cred_t *, - bool_t *, bool_t *, nfs4_ephemeral_tree_t **); -extern void nfs4_ephemeral_umount_unlock(bool_t *, bool_t *, + bool_t *, nfs4_ephemeral_tree_t **); +extern void nfs4_ephemeral_umount_unlock(bool_t *, nfs4_ephemeral_tree_t **); extern int nfs4_record_ephemeral_mount(mntinfo4_t *mi, vnode_t *mvp); +extern int nfs4_callmapid(utf8string *, struct nfs_fsl_info *); +extern int nfs4_fetch_locations(mntinfo4_t *, struct nfs4_sharedfh *, + char *, cred_t *, nfs4_ga_res_t *, COMPOUND4res_clnt *, bool_t); + extern int wait_for_recall(vnode_t *, vnode_t *, nfs4_op_hint_t, nfs4_recov_state_t *); extern void nfs4_end_op_recall(vnode_t *, vnode_t *, nfs4_recov_state_t *); @@ -2138,6 +2159,10 @@ extern char *fn_path(nfs4_fname_t *); extern void fn_move(nfs4_fname_t *, nfs4_fname_t *, char *); extern nfs4_fname_t *fn_parent(nfs4_fname_t *); +/* Referral Support */ +extern int nfs4_process_referral(mntinfo4_t *, nfs4_sharedfh_t *, char *, + cred_t *, nfs4_ga_res_t *, COMPOUND4res_clnt *, struct nfs_fsl_info *); + #endif /* diff --git a/usr/src/uts/common/nfs/nfs4_kprot.h b/usr/src/uts/common/nfs/nfs4_kprot.h index 3e35d29f25..be30bed10c 100644 --- a/usr/src/uts/common/nfs/nfs4_kprot.h +++ b/usr/src/uts/common/nfs/nfs4_kprot.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -227,6 +227,15 @@ struct fs_locations4 { }; typedef struct fs_locations4 fs_locations4; +struct nfs_fsl_info { + uint_t netbuf_len; + uint_t netnm_len; + uint_t knconf_len; + char *netname; + struct netbuf *addr; + struct knetconfig *knconf; +}; + /* * ACL support */ @@ -590,6 +599,7 @@ struct nfs_client_id4 { verifier4 verifier; uint_t id_len; char *id_val; + struct sockaddr *cl_addr; }; typedef struct nfs_client_id4 nfs_client_id4; @@ -1636,6 +1646,12 @@ extern bool_t xdr_CB_COMPOUND4args_clnt(XDR *, CB_COMPOUND4args *); extern bool_t xdr_CB_COMPOUND4args_srv(XDR *, CB_COMPOUND4args *); extern bool_t xdr_CB_COMPOUND4res(XDR *, CB_COMPOUND4res *); +/* + * xdr for referrrals upcall + */ +extern bool_t xdr_knetconfig(XDR *, struct knetconfig *); +extern bool_t xdr_nfs_fsl_info(XDR *, struct nfs_fsl_info *); + #ifdef __cplusplus } diff --git a/usr/src/uts/common/nfs/nfsid_map.h b/usr/src/uts/common/nfs/nfsid_map.h index c617695245..197970d4d2 100644 --- a/usr/src/uts/common/nfs/nfsid_map.h +++ b/usr/src/uts/common/nfs/nfsid_map.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _NFSID_MAP_H #define _NFSID_MAP_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifndef _KERNEL #include <stddef.h> #endif @@ -59,6 +56,7 @@ extern "C" { #define NFSMAPID_UID_STR 2 #define NFSMAPID_STR_GID 3 #define NFSMAPID_GID_STR 4 +#define NFSMAPID_SRV_NETINFO 5 /* * We are passing in arguments in a variable length struct @@ -145,6 +143,35 @@ typedef struct mapid_res mapid_res_t; #define MAPID_RES_LEN(str_length) \ ((offsetof(mapid_res_t, str[0]) + 1 + (str_length) + 7) & ~ 7) +/* + * Support for referral name resolution by the NFS client + */ +typedef struct refd_door_args { + int cmd; /* NFS4_FS_LOCATIONS/NFS4_SRV_NETINFO */ + int xdr_len; /* Length of xdr Buffer */ + char xdr_arg[1]; /* Buffer holding xdr encoded data */ +} refd_door_args_t; + +typedef struct refd_door_res { + int res_status; + int xdr_len; + char xdr_res[1]; +} refd_door_res_t; + +#ifdef _SYSCALL32 +typedef struct refd_door_args32 { + int32_t cmd; + int32_t xdr_len; + char xdr_arg[1]; +} refd_door_args32_t; + +typedef struct refd_door_res32 { + int32_t res_status; + int32_t xdr_len; + char xdr_res[1]; +} refd_door_res32_t; +#endif + #ifdef __cplusplus } #endif diff --git a/usr/src/uts/common/nfs/rnode4.h b/usr/src/uts/common/nfs/rnode4.h index a8d6c5ac71..61ea044ca5 100644 --- a/usr/src/uts/common/nfs/rnode4.h +++ b/usr/src/uts/common/nfs/rnode4.h @@ -41,7 +41,8 @@ extern "C" { typedef enum nfs4_stub_type { NFS4_STUB_NONE, - NFS4_STUB_MIRRORMOUNT + NFS4_STUB_MIRRORMOUNT, + NFS4_STUB_REFERRAL } nfs4_stub_type_t; typedef enum nfs4_access_type { @@ -333,7 +334,7 @@ typedef struct rnode4 { /* sv_fsid (servinfo4_t) to see why */ /* stub type was set */ nfs4_stub_type_t r_stub_type; - /* e.g. mirror-mount */ + /* e.g. mirror-mount or referral */ uint_t r_inmap; /* to serialize read/write and mmap */ } rnode4_t; @@ -371,6 +372,7 @@ typedef struct rnode4 { #define RP_ISSTUB(rp) (((rp)->r_stub_type != NFS4_STUB_NONE)) #define RP_ISSTUB_MIRRORMOUNT(rp) ((rp)->r_stub_type == NFS4_STUB_MIRRORMOUNT) +#define RP_ISSTUB_REFERRAL(rp) ((rp)->r_stub_type == NFS4_STUB_REFERRAL) /* * Open file instances. @@ -415,6 +417,7 @@ extern vnode_t *makenfs4node_by_fh(nfs4_sharedfh_t *, nfs4_sharedfh_t *, extern nfs4_opinst_t *r4mkopenlist(struct mntinfo4 *); extern void r4releopenlist(nfs4_opinst_t *); +extern int r4find_by_fsid(mntinfo4_t *, fattr4_fsid *); /* Access cache calls */ extern nfs4_access_type_t nfs4_access_check(rnode4_t *, uint32_t, cred_t *); @@ -499,6 +502,7 @@ extern rddir4_cache *rddir4_cache_lookup(rnode4_t *, offset_t, int); extern void rddir4_cache_rele(rnode4_t *, rddir4_cache *); extern void r4_stub_mirrormount(rnode4_t *); +extern void r4_stub_referral(rnode4_t *); extern void r4_stub_none(rnode4_t *); #ifdef DEBUG diff --git a/usr/src/uts/common/sys/mkdev.h b/usr/src/uts/common/sys/mkdev.h index 8969362360..0b0f7deb64 100644 --- a/usr/src/uts/common/sys/mkdev.h +++ b/usr/src/uts/common/sys/mkdev.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 1997 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -30,8 +29,6 @@ #ifndef _SYS_MKDEV_H #define _SYS_MKDEV_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #ifdef __cplusplus @@ -54,10 +51,11 @@ extern "C" { #define MAXMAJ32 0x3ffful /* SVR4 max major value */ #define MAXMIN32 0x3fffful /* SVR4 max minor value */ -#ifdef _LP64 - #define NBITSMAJOR64 32 /* # of major device bits in 64-bit Solaris */ #define NBITSMINOR64 32 /* # of minor device bits in 64-bit Solaris */ + +#ifdef _LP64 + #define MAXMAJ64 0xfffffffful /* max major value */ #define MAXMIN64 0xfffffffful /* max minor value */ diff --git a/usr/src/uts/common/sys/vnode.h b/usr/src/uts/common/sys/vnode.h index cdf4fe9b68..97504aabf3 100644 --- a/usr/src/uts/common/sys/vnode.h +++ b/usr/src/uts/common/sys/vnode.h @@ -1239,6 +1239,7 @@ vnode_t *specvp(struct vnode *vp, dev_t dev, vtype_t type, struct cred *cr); vnode_t *makespecvp(dev_t dev, vtype_t type); vn_vfslocks_entry_t *vn_vfslocks_getlock(void *); void vn_vfslocks_rele(vn_vfslocks_entry_t *); +boolean_t vn_is_reparse(vnode_t *, cred_t *, caller_context_t *); void vn_copypath(struct vnode *src, struct vnode *dst); void vn_setpath_str(struct vnode *vp, const char *str, size_t len); |