#include <sys/types.h>
#include <sys/debug.h>
#include <sys/stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_oplock.h>
extern const char *xlate_nt_status(uint32_t);
#define OPLOCK_CACHE_RWH (READ_CACHING | HANDLE_CACHING | WRITE_CACHING)
#define OPLOCK_TYPE (LEVEL_TWO_OPLOCK | LEVEL_ONE_OPLOCK |\
BATCH_OPLOCK | OPLOCK_LEVEL_GRANULAR)
#define MAXFID 10
smb_node_t root_node, test_node;
smb_ofile_t ofile_array[MAXFID];
smb_request_t test_sr;
uint32_t last_ind_break_level;
char cmdbuf[100];
static void run_ind_break_in_ack(smb_ofile_t *);
#define BIT_DEF(name) { name, #name }
struct bit_defs {
uint32_t mask;
const char *name;
} state_bits[] = {
BIT_DEF(NO_OPLOCK),
BIT_DEF(BREAK_TO_NO_CACHING),
BIT_DEF(BREAK_TO_WRITE_CACHING),
BIT_DEF(BREAK_TO_HANDLE_CACHING),
BIT_DEF(BREAK_TO_READ_CACHING),
BIT_DEF(BREAK_TO_TWO_TO_NONE),
BIT_DEF(BREAK_TO_NONE),
BIT_DEF(BREAK_TO_TWO),
BIT_DEF(BATCH_OPLOCK),
BIT_DEF(LEVEL_ONE_OPLOCK),
BIT_DEF(LEVEL_TWO_OPLOCK),
BIT_DEF(MIXED_R_AND_RH),
BIT_DEF(EXCLUSIVE),
BIT_DEF(WRITE_CACHING),
BIT_DEF(HANDLE_CACHING),
BIT_DEF(READ_CACHING),
{ 0, NULL }
};
static void
print_bits32(char *label, struct bit_defs *bit, uint32_t state)
{
printf("%s0x%x (", label, state);
while (bit->mask != 0) {
if ((state & bit->mask) != 0)
printf(" %s", bit->name);
bit++;
}
printf(" )\n");
}
const char helpstr[] = "Commands:\n"
"help\t\tList commands\n"
"show\t\tShow OpLock state etc.\n"
"open FID\n"
"close FID\n"
"req FID [OplockLevel]\n"
"ack FID [OplockLevel]\n"
"brk-parent FID\n"
"brk-open [OverWrite]\n"
"brk-handle FID\n"
"brk-read FID\n"
"brk-write FID\n"
"brk-setinfo FID [InfoClass]\n"
"move FID1 FID2\n"
"waiters FID [count]\n";
static void
do_show(void)
{
smb_node_t *node = &test_node;
smb_oplock_t *ol = &node->n_oplock;
uint32_t state = ol->ol_state;
smb_ofile_t *f;
print_bits32(" ol_state=", state_bits, state);
if (ol->excl_open != NULL)
printf(" Excl=Y (FID=%d)", ol->excl_open->f_fid);
else
printf(" Excl=n");
printf(" cnt_II=%d cnt_R=%d cnt_RH=%d cnt_RHBQ=%d\n",
ol->cnt_II, ol->cnt_R, ol->cnt_RH, ol->cnt_RHBQ);
printf(" ofile_cnt=%d\n", node->n_ofile_list.ll_count);
FOREACH_NODE_OFILE(node, f) {
smb_oplock_grant_t *og = &f->f_oplock;
printf(" fid=%d Lease=%s State=0x%x",
f->f_fid,
f->TargetOplockKey,
og->og_state);
if (og->og_breaking)
printf(" BreakTo=0x%x", og->og_breakto);
printf(" Excl=%s onlist:",
(ol->excl_open == f) ? "Y" : "N");
if (og->onlist_II)
printf(" II");
if (og->onlist_R)
printf(" R");
if (og->onlist_RH)
printf(" RH");
if (og->onlist_RHBQ) {
printf(" RHBQ(to %s)",
og->BreakingToRead ?
"read" : "none");
}
printf("\n");
}
}
static void
do_open(int fid, char *arg2)
{
smb_node_t *node = &test_node;
smb_ofile_t *ofile = &ofile_array[fid];
if (ofile->f_refcnt) {
printf("open fid %d already opened\n");
return;
}
if (arg2 != NULL) {
(void) strlcpy((char *)ofile->TargetOplockKey, arg2,
SMB_LEASE_KEY_SZ);
}
ofile->f_refcnt++;
node->n_open_count++;
smb_llist_insert_tail(&node->n_ofile_list, ofile);
printf(" open %d OK\n", fid);
}
static void
do_close(int fid)
{
smb_node_t *node = &test_node;
smb_ofile_t *ofile = &ofile_array[fid];
if (ofile->f_refcnt <= 0) {
printf(" close fid %d already closed\n");
return;
}
smb_llist_enter(&node->n_ofile_list, RW_READER);
mutex_enter(&node->n_oplock.ol_mutex);
smb_oplock_break_CLOSE(ofile->f_node, ofile);
smb_llist_remove(&node->n_ofile_list, ofile);
node->n_open_count--;
mutex_exit(&node->n_oplock.ol_mutex);
smb_llist_exit(&node->n_ofile_list);
ofile->f_refcnt--;
bzero(ofile->TargetOplockKey, SMB_LEASE_KEY_SZ);
printf(" close OK\n");
}
static void
do_req(int fid, char *arg2)
{
smb_ofile_t *ofile = &ofile_array[fid];
uint32_t oplock = BATCH_OPLOCK;
uint32_t status;
if (arg2 != NULL)
oplock = strtol(arg2, NULL, 16);
status = smb_oplock_request(&test_sr, ofile, &oplock);
if (status == 0 ||
status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
ofile->f_oplock.og_state = oplock;
ofile->f_oplock.og_breakto = oplock;
ofile->f_oplock.og_breaking = B_FALSE;
}
printf(" req oplock fid=%d ret oplock=0x%x status=0x%x (%s)\n",
fid, oplock, status, xlate_nt_status(status));
}
static void
do_ack(int fid, char *arg2)
{
smb_node_t *node = &test_node;
smb_ofile_t *ofile = &ofile_array[fid];
uint32_t oplock;
uint32_t status;
oplock = last_ind_break_level;
if (arg2 != NULL)
oplock = strtol(arg2, NULL, 16);
smb_llist_enter(&node->n_ofile_list, RW_READER);
mutex_enter(&node->n_oplock.ol_mutex);
ofile->f_oplock.og_breaking = 0;
status = smb_oplock_ack_break(&test_sr, ofile, &oplock);
if (status == 0)
ofile->f_oplock.og_state = oplock;
mutex_exit(&node->n_oplock.ol_mutex);
smb_llist_exit(&node->n_ofile_list);
if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
printf(" ack: break fid=%d, break-in-progress\n", fid);
ASSERT(0);
}
printf(" ack: break fid=%d, newstate=0x%x, status=0x%x (%s)\n",
fid, oplock, status, xlate_nt_status(status));
run_ind_break_in_ack(ofile);
}
static void
do_brk_parent(int fid)
{
smb_ofile_t *ofile = &ofile_array[fid];
uint32_t status;
status = smb_oplock_break_PARENT(&test_node, ofile);
printf(" brk-parent %d ret status=0x%x (%s)\n",
fid, status, xlate_nt_status(status));
}
static void
do_brk_open(int fid, char *arg2)
{
smb_ofile_t *ofile = &ofile_array[fid];
uint32_t status;
int disp = FILE_OPEN;
if (arg2 != NULL)
disp = strtol(arg2, NULL, 16);
status = smb_oplock_break_OPEN(&test_node, ofile, 7, disp);
printf(" brk-open %d ret status=0x%x (%s)\n",
fid, status, xlate_nt_status(status));
}
static void
do_brk_handle(int fid)
{
smb_ofile_t *ofile = &ofile_array[fid];
uint32_t status;
status = smb_oplock_break_HANDLE(&test_node, ofile);
printf(" brk-handle %d ret status=0x%x (%s)\n",
fid, status, xlate_nt_status(status));
}
static void
do_brk_read(int fid)
{
smb_ofile_t *ofile = &ofile_array[fid];
uint32_t status;
status = smb_oplock_break_READ(ofile->f_node, ofile);
printf(" brk-read %d ret status=0x%x (%s)\n",
fid, status, xlate_nt_status(status));
}
static void
do_brk_write(int fid)
{
smb_ofile_t *ofile = &ofile_array[fid];
uint32_t status;
status = smb_oplock_break_WRITE(ofile->f_node, ofile);
printf(" brk-write %d ret status=0x%x (%s)\n",
fid, status, xlate_nt_status(status));
}
static void
do_brk_setinfo(int fid, char *arg2)
{
smb_ofile_t *ofile = &ofile_array[fid];
uint32_t status;
int infoclass = FileEndOfFileInformation;
if (arg2 != NULL)
infoclass = strtol(arg2, NULL, 16);
status = smb_oplock_break_SETINFO(
&test_node, ofile, infoclass);
printf(" brk-setinfo %d 0x%x ret status=0x%x (%s)\n",
fid, infoclass, status, xlate_nt_status(status));
}
static void
do_move(int fid, char *arg2)
{
smb_node_t *node = &test_node;
smb_ofile_t *ofile = &ofile_array[fid];
smb_ofile_t *of2;
int fid2;
if (arg2 == NULL) {
fprintf(stderr, "move: FID2 required\n");
return;
}
fid2 = atoi(arg2);
if (fid2 <= 0 || fid2 >= MAXFID) {
fprintf(stderr, "move: bad FID2 %d\n", fid2);
return;
}
of2 = &ofile_array[fid2];
mutex_enter(&node->n_oplock.ol_mutex);
smb_oplock_move(&test_node, ofile, of2);
mutex_exit(&node->n_oplock.ol_mutex);
printf(" move %d %d\n", fid, fid2);
}
static void
do_waiters(int fid, char *arg2)
{
smb_node_t *node = &test_node;
smb_oplock_t *ol = &node->n_oplock;
int old, new = 0;
if (arg2 != NULL)
new = atoi(arg2);
old = ol->waiters;
ol->waiters = new;
printf(" waiters %d -> %d\n", old, new);
}
int
main(int argc, char *argv[])
{
smb_node_t *node = &test_node;
char *cmd;
char *arg1;
char *arg2;
char *savep;
char *sep = " \t\n";
char *prompt = NULL;
int fid;
if (isatty(0))
prompt = "> ";
mutex_init(&node->n_mutex, NULL, MUTEX_DEFAULT, NULL);
smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t),
offsetof(smb_ofile_t, f_node_lnd));
for (fid = 0; fid < MAXFID; fid++) {
smb_ofile_t *f = &ofile_array[fid];
f->f_magic = SMB_OFILE_MAGIC;
mutex_init(&f->f_mutex, NULL, MUTEX_DEFAULT, NULL);
f->f_fid = fid;
f->f_ftype = SMB_FTYPE_DISK;
f->f_node = &test_node;
}
for (;;) {
if (prompt) {
(void) fputs(prompt, stdout);
(void) fflush(stdout);
}
cmd = fgets(cmdbuf, sizeof (cmdbuf), stdin);
if (cmd == NULL)
break;
if (cmd[0] == '#')
continue;
if (prompt == NULL) {
(void) fputs(cmdbuf, stdout);
}
cmd = strtok_r(cmd, sep, &savep);
if (cmd == NULL)
continue;
if (0 == strcmp(cmd, "help")) {
(void) fputs(helpstr, stdout);
continue;
}
if (0 == strcmp(cmd, "show")) {
do_show();
continue;
}
arg1 = strtok_r(NULL, sep, &savep);
if (arg1 == NULL) {
fprintf(stderr, "%s missing arg1\n", cmd);
continue;
}
fid = atoi(arg1);
if (fid <= 0 || fid >= MAXFID) {
fprintf(stderr, "%s bad FID %d\n", cmd, fid);
continue;
}
if (0 == strcmp(cmd, "close")) {
do_close(fid);
continue;
}
if (0 == strcmp(cmd, "brk-parent")) {
do_brk_parent(fid);
continue;
}
if (0 == strcmp(cmd, "brk-handle")) {
do_brk_handle(fid);
continue;
}
if (0 == strcmp(cmd, "brk-read")) {
do_brk_read(fid);
continue;
}
if (0 == strcmp(cmd, "brk-write")) {
do_brk_write(fid);
continue;
}
arg2 = strtok_r(NULL, sep, &savep);
if (0 == strcmp(cmd, "open")) {
do_open(fid, arg2);
continue;
}
if (0 == strcmp(cmd, "req")) {
do_req(fid, arg2);
continue;
}
if (0 == strcmp(cmd, "ack")) {
do_ack(fid, arg2);
continue;
}
if (0 == strcmp(cmd, "brk-open")) {
do_brk_open(fid, arg2);
continue;
}
if (0 == strcmp(cmd, "brk-setinfo")) {
do_brk_setinfo(fid, arg2);
continue;
}
if (0 == strcmp(cmd, "move")) {
do_move(fid, arg2);
continue;
}
if (0 == strcmp(cmd, "waiters")) {
do_waiters(fid, arg2);
continue;
}
fprintf(stderr, "%s unknown command. Try help\n", cmd);
}
return (0);
}
boolean_t
smb_node_is_file(smb_node_t *node)
{
return (B_TRUE);
}
boolean_t
smb_ofile_is_open(smb_ofile_t *ofile)
{
return (ofile->f_refcnt != 0);
}
int
smb_lock_range_access(
smb_request_t *sr,
smb_node_t *node,
uint64_t start,
uint64_t length,
boolean_t will_write)
{
return (0);
}
static void
test_oplock_send_break(smb_ofile_t *ofile,
uint32_t NewLevel, boolean_t AckReq)
{
smb_oplock_grant_t *og = &ofile->f_oplock;
uint32_t OldLevel;
if ((og->og_state & OPLOCK_LEVEL_GRANULAR) != 0)
NewLevel |= OPLOCK_LEVEL_GRANULAR;
OldLevel = og->og_state;
og->og_breakto = NewLevel;
og->og_breaking = B_TRUE;
printf("*smb_oplock_send_break fid=%d "
"NewLevel=0x%x, OldLevel=0x%x, AckReq=%d)\n",
ofile->f_fid, NewLevel, OldLevel, AckReq);
if (!AckReq) {
og->og_state = NewLevel;
og->og_breaking = B_FALSE;
}
last_ind_break_level = NewLevel;
}
void
smb_oplock_ind_break(smb_ofile_t *ofile, uint32_t NewLevel,
boolean_t AckReq, uint32_t status)
{
smb_oplock_grant_t *og = &ofile->f_oplock;
printf("*smb_oplock_ind_break fid=%d NewLevel=0x%x,"
" AckReq=%d, ComplStatus=0x%x (%s)\n",
ofile->f_fid, NewLevel, AckReq,
status, xlate_nt_status(status));
switch (status) {
case NT_STATUS_SUCCESS:
case NT_STATUS_CANNOT_GRANT_REQUESTED_OPLOCK:
test_oplock_send_break(ofile, NewLevel, AckReq);
break;
case NT_STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE:
case NT_STATUS_OPLOCK_HANDLE_CLOSED:
og->og_state = OPLOCK_LEVEL_NONE;
og->og_breakto = OPLOCK_LEVEL_NONE;
og->og_breaking = B_FALSE;
break;
default:
ASSERT(0);
break;
}
}
static uint32_t break_in_ack_NewLevel;
static boolean_t break_in_ack_AckReq;
static boolean_t break_in_ack_called;
void
smb_oplock_ind_break_in_ack(smb_request_t *sr, smb_ofile_t *ofile,
uint32_t NewLevel, boolean_t AckRequired)
{
ASSERT(sr == &test_sr);
ASSERT(!break_in_ack_called);
break_in_ack_called = B_TRUE;
break_in_ack_NewLevel = NewLevel;
break_in_ack_AckReq = AckRequired;
}
static void
run_ind_break_in_ack(smb_ofile_t *ofile)
{
uint32_t NewLevel;
boolean_t AckReq;
if (!break_in_ack_called)
return;
break_in_ack_called = B_FALSE;
NewLevel = break_in_ack_NewLevel;
AckReq = break_in_ack_AckReq;
printf("*smb_oplock_ind_break_in_ack fid=%d NewLevel=0x%x,"
" AckReq=%d\n",
ofile->f_fid, NewLevel, AckReq);
test_oplock_send_break(ofile, NewLevel, AckReq);
}
uint32_t
smb_oplock_wait_break(smb_request_t *sr, smb_node_t *node, int timeout)
{
printf("*smb_oplock_wait_break (state=0x%x)\n",
node->n_oplock.ol_state);
return (0);
}
int
smb_fem_oplock_install(smb_node_t *node)
{
return (0);
}
void
smb_fem_oplock_uninstall(smb_node_t *node)
{
}
void
__dtrace_fksmb___probe1(char *n, unsigned long a)
{
}
void
__dtrace_fksmb___probe2(char *n, unsigned long a, unsigned long b)
{
}