1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
14  */
15 
16 /*
17  * Test & debug program for oplocks
18  *
19  * This implements a simple command reader which accepts
20  * commands to simulate oplock events, and prints the
21  * state changes and actions that would happen after
22  * each event.
23  */
24 
25 #include <sys/types.h>
26 #include <sys/debug.h>
27 #include <sys/stddef.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <strings.h>
32 #include <unistd.h>
33 
34 #include <smbsrv/smb_kproto.h>
35 #include <smbsrv/smb_oplock.h>
36 
37 #define	OPLOCK_CACHE_RWH	(READ_CACHING | HANDLE_CACHING | WRITE_CACHING)
38 #define	OPLOCK_TYPE	(LEVEL_TWO_OPLOCK | LEVEL_ONE_OPLOCK |\
39 			BATCH_OPLOCK | OPLOCK_LEVEL_GRANULAR)
40 
41 #define	MAXFID 10
42 
43 smb_node_t root_node, test_node;
44 smb_ofile_t  ofile_array[MAXFID];
45 smb_request_t test_sr;
46 uint32_t last_ind_break_level;
47 char cmdbuf[100];
48 
49 extern const char *xlate_nt_status(uint32_t);
50 
51 #define	BIT_DEF(name) { name, #name }
52 
53 struct bit_defs {
54 	uint32_t mask;
55 	const char *name;
56 } state_bits[] = {
57 	BIT_DEF(NO_OPLOCK),
58 	BIT_DEF(BREAK_TO_NO_CACHING),
59 	BIT_DEF(BREAK_TO_WRITE_CACHING),
60 	BIT_DEF(BREAK_TO_HANDLE_CACHING),
61 	BIT_DEF(BREAK_TO_READ_CACHING),
62 	BIT_DEF(BREAK_TO_TWO_TO_NONE),
63 	BIT_DEF(BREAK_TO_NONE),
64 	BIT_DEF(BREAK_TO_TWO),
65 	BIT_DEF(BATCH_OPLOCK),
66 	BIT_DEF(LEVEL_ONE_OPLOCK),
67 	BIT_DEF(LEVEL_TWO_OPLOCK),
68 	BIT_DEF(MIXED_R_AND_RH),
69 	BIT_DEF(EXCLUSIVE),
70 	BIT_DEF(WRITE_CACHING),
71 	BIT_DEF(HANDLE_CACHING),
72 	BIT_DEF(READ_CACHING),
73 	{ 0, NULL }
74 };
75 
76 /*
77  * Helper to print flags fields
78  */
79 static void
80 print_bits32(char *label, struct bit_defs *bit, uint32_t state)
81 {
82 	printf("%s0x%x (", label, state);
83 	while (bit->mask != 0) {
84 		if ((state & bit->mask) != 0)
85 			printf(" %s", bit->name);
86 		bit++;
87 	}
88 	printf(" )\n");
89 }
90 
91 /*
92  * Command language:
93  *
94  */
95 const char helpstr[] = "Commands:\n"
96 	"help\t\tList commands\n"
97 	"show\t\tShow OpLock state etc.\n"
98 	"open FID\n"
99 	"close FID\n"
100 	"req FID [OplockLevel]\n"
101 	"ack FID [OplockLevel]\n"
102 	"brk-parent FID\n"
103 	"brk-open [OverWrite]\n"
104 	"brk-handle FID\n"
105 	"brk-read FID\n"
106 	"brk-write FID\n"
107 	"brk-setinfo FID [InfoClass]\n"
108 	"move FID1 FID2\n"
109 	"waiters FID [count]\n";
110 
111 /*
112  * Command handlers
113  */
114 
115 static void
116 do_show(void)
117 {
118 	smb_node_t *node = &test_node;
119 	smb_oplock_t *ol = &node->n_oplock;
120 	uint32_t state = ol->ol_state;
121 	smb_ofile_t *f;
122 
123 	print_bits32(" ol_state=", state_bits, state);
124 
125 	if (ol->excl_open != NULL)
126 		printf(" Excl=Y (FID=%d)", ol->excl_open->f_fid);
127 	else
128 		printf(" Excl=n");
129 	printf(" cnt_II=%d cnt_R=%d cnt_RH=%d cnt_RHBQ=%d\n",
130 	    ol->cnt_II, ol->cnt_R, ol->cnt_RH, ol->cnt_RHBQ);
131 
132 	printf(" ofile_cnt=%d\n", node->n_ofile_list.ll_count);
133 	FOREACH_NODE_OFILE(node, f) {
134 		smb_oplock_grant_t *og = &f->f_oplock;
135 		printf("  fid=%d Lease=%s OgState=0x%x Brk=0x%x",
136 		    f->f_fid,
137 		    f->TargetOplockKey,	/* lease */
138 		    f->f_oplock.og_state,
139 		    f->f_oplock.og_breaking);
140 		printf(" Excl=%s onlist: %s %s %s",
141 		    (ol->excl_open == f) ? "Y" : "N",
142 		    og->onlist_II ? "II" : "",
143 		    og->onlist_R  ? "R" : "",
144 		    og->onlist_RH ? "RH" : "");
145 		if (og->onlist_RHBQ) {
146 			printf(" RHBQ(to %s)",
147 			    og->BreakingToRead ?
148 			    "read" : "none");
149 		}
150 		printf("\n");
151 	}
152 }
153 
154 static void
155 do_open(int fid, char *arg2)
156 {
157 	smb_node_t *node = &test_node;
158 	smb_ofile_t *ofile = &ofile_array[fid];
159 
160 	/*
161 	 * Simulate an open (minimal init)
162 	 */
163 	if (ofile->f_refcnt) {
164 		printf("open fid %d already opened\n");
165 		return;
166 	}
167 
168 	if (arg2 != NULL)
169 		strlcpy((char *)ofile->TargetOplockKey, arg2,
170 		    SMB_LEASE_KEY_SZ);
171 
172 	ofile->f_refcnt++;
173 	node->n_open_count++;
174 	smb_llist_insert_tail(&node->n_ofile_list, ofile);
175 	printf(" open %d OK\n", fid);
176 }
177 
178 static void
179 do_close(int fid)
180 {
181 	smb_node_t *node = &test_node;
182 	smb_ofile_t *ofile = &ofile_array[fid];
183 
184 	/*
185 	 * Simulate an close
186 	 */
187 	if (ofile->f_refcnt <= 0) {
188 		printf(" close fid %d already closed\n");
189 		return;
190 	}
191 	smb_oplock_break_CLOSE(ofile->f_node, ofile);
192 
193 	smb_llist_remove(&node->n_ofile_list, ofile);
194 	node->n_open_count--;
195 	ofile->f_refcnt--;
196 
197 	bzero(ofile->TargetOplockKey, SMB_LEASE_KEY_SZ);
198 
199 	printf(" close OK\n");
200 }
201 
202 static void
203 do_req(int fid, char *arg2)
204 {
205 	smb_ofile_t *ofile = &ofile_array[fid];
206 	uint32_t oplock = BATCH_OPLOCK;
207 	uint32_t status;
208 
209 	if (arg2 != NULL)
210 		oplock = strtol(arg2, NULL, 16);
211 
212 	/*
213 	 * Request an oplock
214 	 */
215 	status = smb_oplock_request(&test_sr, ofile, &oplock);
216 	if (status == 0)
217 		ofile->f_oplock.og_state = oplock;
218 	printf(" req oplock fid=%d ret oplock=0x%x status=0x%x (%s)\n",
219 	    fid, oplock, status, xlate_nt_status(status));
220 }
221 
222 
223 static void
224 do_ack(int fid, char *arg2)
225 {
226 	smb_ofile_t *ofile = &ofile_array[fid];
227 	uint32_t oplock;
228 	uint32_t status;
229 
230 	/* Default to level in last smb_oplock_ind_break() */
231 	oplock = last_ind_break_level;
232 	if (arg2 != NULL)
233 		oplock = strtol(arg2, NULL, 16);
234 
235 	ofile->f_oplock.og_breaking = 0;
236 	status = smb_oplock_ack_break(&test_sr, ofile, &oplock);
237 	if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
238 		printf(" ack: break fid=%d, break-in-progress\n", fid);
239 		ofile->f_oplock.og_state = oplock;
240 	}
241 	if (status == 0)
242 		ofile->f_oplock.og_state = oplock;
243 
244 	printf(" ack: break fid=%d, newstate=0x%x, status=0x%x (%s)\n",
245 	    fid, oplock, status, xlate_nt_status(status));
246 }
247 
248 static void
249 do_brk_parent(int fid)
250 {
251 	smb_ofile_t *ofile = &ofile_array[fid];
252 	uint32_t status;
253 
254 	status = smb_oplock_break_PARENT(&test_node, ofile);
255 	printf(" brk-parent %d ret status=0x%x (%s)\n",
256 	    fid, status, xlate_nt_status(status));
257 }
258 
259 static void
260 do_brk_open(int fid, char *arg2)
261 {
262 	smb_ofile_t *ofile = &ofile_array[fid];
263 	uint32_t status;
264 	int disp = FILE_OPEN;
265 
266 	if (arg2 != NULL)
267 		disp = strtol(arg2, NULL, 16);
268 
269 	status = smb_oplock_break_OPEN(&test_node, ofile, 7, disp);
270 	printf(" brk-open %d ret status=0x%x (%s)\n",
271 	    fid, status, xlate_nt_status(status));
272 }
273 
274 static void
275 do_brk_handle(int fid)
276 {
277 	smb_ofile_t *ofile = &ofile_array[fid];
278 	uint32_t status;
279 
280 	status = smb_oplock_break_HANDLE(&test_node, ofile);
281 	printf(" brk-handle %d ret status=0x%x (%s)\n",
282 	    fid, status, xlate_nt_status(status));
283 
284 }
285 
286 static void
287 do_brk_read(int fid)
288 {
289 	smb_ofile_t *ofile = &ofile_array[fid];
290 	uint32_t status;
291 
292 	status = smb_oplock_break_READ(ofile->f_node, ofile);
293 	printf(" brk-read %d ret status=0x%x (%s)\n",
294 	    fid, status, xlate_nt_status(status));
295 }
296 
297 static void
298 do_brk_write(int fid)
299 {
300 	smb_ofile_t *ofile = &ofile_array[fid];
301 	uint32_t status;
302 
303 	status = smb_oplock_break_WRITE(ofile->f_node, ofile);
304 	printf(" brk-write %d ret status=0x%x (%s)\n",
305 	    fid, status, xlate_nt_status(status));
306 }
307 
308 static void
309 do_brk_setinfo(int fid, char *arg2)
310 {
311 	smb_ofile_t *ofile = &ofile_array[fid];
312 	uint32_t status;
313 	int infoclass = FileEndOfFileInformation; /* 20 */
314 
315 	if (arg2 != NULL)
316 		infoclass = strtol(arg2, NULL, 16);
317 
318 	status = smb_oplock_break_SETINFO(
319 	    &test_node, ofile, infoclass);
320 	printf(" brk-setinfo %d ret status=0x%x (%s)\n",
321 	    fid, status, xlate_nt_status(status));
322 
323 }
324 
325 /*
326  * Move oplock to another FD, as specified,
327  * or any other available open
328  */
329 static void
330 do_move(int fid, char *arg2)
331 {
332 	smb_ofile_t *ofile = &ofile_array[fid];
333 	smb_ofile_t *of2;
334 	int fid2;
335 
336 	if (arg2 == NULL) {
337 		fprintf(stderr, "move: FID2 required\n");
338 		return;
339 	}
340 	fid2 = atoi(arg2);
341 	if (fid2 <= 0 || fid2 >= MAXFID) {
342 		fprintf(stderr, "move: bad FID2 %d\n", fid2);
343 		return;
344 	}
345 	of2 = &ofile_array[fid2];
346 
347 	smb_oplock_move(&test_node, ofile, of2);
348 	printf(" move %d %d\n", fid, fid2);
349 }
350 
351 /*
352  * Set/clear oplock.waiters, which affects ack-break
353  */
354 static void
355 do_waiters(int fid, char *arg2)
356 {
357 	smb_node_t *node = &test_node;
358 	smb_oplock_t *ol = &node->n_oplock;
359 	int old, new = 0;
360 
361 	if (arg2 != NULL)
362 		new = atoi(arg2);
363 
364 	old = ol->waiters;
365 	ol->waiters = new;
366 
367 	printf(" waiters %d -> %d\n", old, new);
368 }
369 
370 int
371 main(int argc, char *argv[])
372 {
373 	smb_node_t *node = &test_node;
374 	char *cmd;
375 	char *arg1;
376 	char *arg2;
377 	char *savep;
378 	char *sep = " \t\n";
379 	char *prompt = NULL;
380 	int fid;
381 
382 	if (isatty(0))
383 		prompt = "> ";
384 
385 	smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t),
386 	    offsetof(smb_ofile_t, f_node_lnd));
387 
388 	for (fid = 0; fid < MAXFID; fid++) {
389 		smb_ofile_t *f = &ofile_array[fid];
390 
391 		f->f_magic = SMB_OFILE_MAGIC;
392 		mutex_init(&f->f_mutex, NULL, MUTEX_DEFAULT, NULL);
393 		f->f_fid = fid;
394 		f->f_ftype = SMB_FTYPE_DISK;
395 		f->f_node = &test_node;
396 	}
397 
398 	for (;;) {
399 		if (prompt) {
400 			fputs(prompt, stdout);
401 			fflush(stdout);
402 		}
403 
404 		cmd = fgets(cmdbuf, sizeof (cmdbuf), stdin);
405 		if (cmd == NULL)
406 			break;
407 		if (cmd[0] == '#')
408 			continue;
409 
410 		if (prompt == NULL) {
411 			/* Put commands in the output too. */
412 			fputs(cmdbuf, stdout);
413 		}
414 		cmd = strtok_r(cmd, sep, &savep);
415 		if (cmd == NULL)
416 			continue;
417 
418 		/*
419 		 * Commands with no args
420 		 */
421 		if (0 == strcmp(cmd, "help")) {
422 			fputs(helpstr, stdout);
423 			continue;
424 		}
425 
426 		if (0 == strcmp(cmd, "show")) {
427 			do_show();
428 			continue;
429 		}
430 
431 		/*
432 		 * Commands with one arg (the FID)
433 		 */
434 		arg1 = strtok_r(NULL, sep, &savep);
435 		if (arg1 == NULL) {
436 			fprintf(stderr, "%s missing arg1\n", cmd);
437 			continue;
438 		}
439 		fid = atoi(arg1);
440 		if (fid <= 0 || fid >= MAXFID) {
441 			fprintf(stderr, "%s bad FID %d\n", cmd, fid);
442 			continue;
443 		}
444 
445 		if (0 == strcmp(cmd, "close")) {
446 			do_close(fid);
447 			continue;
448 		}
449 		if (0 == strcmp(cmd, "brk-parent")) {
450 			do_brk_parent(fid);
451 			continue;
452 		}
453 		if (0 == strcmp(cmd, "brk-handle")) {
454 			do_brk_handle(fid);
455 			continue;
456 		}
457 		if (0 == strcmp(cmd, "brk-read")) {
458 			do_brk_read(fid);
459 			continue;
460 		}
461 		if (0 == strcmp(cmd, "brk-write")) {
462 			do_brk_write(fid);
463 			continue;
464 		}
465 
466 		/*
467 		 * Commands with an (optional) arg2.
468 		 */
469 		arg2 = strtok_r(NULL, sep, &savep);
470 
471 		if (0 == strcmp(cmd, "open")) {
472 			do_open(fid, arg2);
473 			continue;
474 		}
475 		if (0 == strcmp(cmd, "req")) {
476 			do_req(fid, arg2);
477 			continue;
478 		}
479 		if (0 == strcmp(cmd, "ack")) {
480 			do_ack(fid, arg2);
481 			continue;
482 		}
483 		if (0 == strcmp(cmd, "brk-open")) {
484 			do_brk_open(fid, arg2);
485 			continue;
486 		}
487 		if (0 == strcmp(cmd, "brk-setinfo")) {
488 			do_brk_setinfo(fid, arg2);
489 			continue;
490 		}
491 		if (0 == strcmp(cmd, "move")) {
492 			do_move(fid, arg2);
493 			continue;
494 		}
495 		if (0 == strcmp(cmd, "waiters")) {
496 			do_waiters(fid, arg2);
497 			continue;
498 		}
499 
500 		fprintf(stderr, "%s unknown command. Try help\n", cmd);
501 	}
502 	return (0);
503 }
504 
505 /*
506  * A few functions called by the oplock code
507  * Stubbed out, and/or just print a message.
508  */
509 
510 boolean_t
511 smb_node_is_file(smb_node_t *node)
512 {
513 	return (B_TRUE);
514 }
515 
516 boolean_t
517 smb_ofile_is_open(smb_ofile_t *ofile)
518 {
519 	return (ofile->f_refcnt != 0);
520 }
521 
522 int
523 smb_lock_range_access(
524     smb_request_t	*sr,
525     smb_node_t		*node,
526     uint64_t		start,
527     uint64_t		length,
528     boolean_t		will_write)
529 {
530 	return (0);
531 }
532 
533 /*
534  * Test code replacement for: smb_oplock_send_brk()
535  */
536 static void
537 test_oplock_send_brk(smb_ofile_t *ofile,
538     uint32_t NewLevel, boolean_t AckReq)
539 {
540 	smb_oplock_grant_t *og = &ofile->f_oplock;
541 
542 	/* Skip building a message. */
543 
544 	if ((og->og_state & OPLOCK_LEVEL_GRANULAR) != 0)
545 		NewLevel |= OPLOCK_LEVEL_GRANULAR;
546 
547 	/*
548 	 * In a real server, we would send a break to the client,
549 	 * and keep track (at the SMB level) whether this oplock
550 	 * was obtained via a lease or an old-style oplock.
551 	 */
552 	if (AckReq) {
553 		uint32_t BreakTo;
554 
555 		if ((og->og_state & OPLOCK_LEVEL_GRANULAR) != 0) {
556 
557 			BreakTo = (NewLevel & CACHE_RWH) << BREAK_SHIFT;
558 			if (BreakTo == 0)
559 				BreakTo = BREAK_TO_NO_CACHING;
560 		} else {
561 			if ((NewLevel & LEVEL_TWO_OPLOCK) != 0)
562 				BreakTo = BREAK_TO_TWO;
563 			else
564 				BreakTo = BREAK_TO_NONE;
565 		}
566 		og->og_breaking = BreakTo;
567 		last_ind_break_level = NewLevel;
568 		/* Set og_state in  do_ack */
569 	} else {
570 		og->og_state = NewLevel;
571 		/* Clear og_breaking in do_ack */
572 	}
573 }
574 
575 /*
576  * Simplified version of what's in smb_srv_oplock.c
577  */
578 void
579 smb_oplock_ind_break(smb_ofile_t *ofile, uint32_t NewLevel,
580     boolean_t AckReq, uint32_t status)
581 {
582 	smb_oplock_grant_t *og = &ofile->f_oplock;
583 
584 	printf("*smb_oplock_ind_break fid=%d NewLevel=0x%x,"
585 	    " AckReq=%d, ComplStatus=0x%x (%s)\n",
586 	    ofile->f_fid, NewLevel, AckReq,
587 	    status, xlate_nt_status(status));
588 
589 	/*
590 	 * Note that the CompletionStatus from the FS level
591 	 * (smb_cmn_oplock.c) encodes what kind of action we
592 	 * need to take at the SMB level.
593 	 */
594 	switch (status) {
595 
596 	case NT_STATUS_SUCCESS:
597 	case NT_STATUS_CANNOT_GRANT_REQUESTED_OPLOCK:
598 		test_oplock_send_brk(ofile, NewLevel, AckReq);
599 		break;
600 
601 	case NT_STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE:
602 	case NT_STATUS_OPLOCK_HANDLE_CLOSED:
603 		og->og_state = OPLOCK_LEVEL_NONE;
604 		break;
605 
606 	default:
607 		/* Checked by caller. */
608 		ASSERT(0);
609 		break;
610 	}
611 }
612 
613 void
614 smb_oplock_ind_break_in_ack(smb_request_t *sr, smb_ofile_t *ofile,
615     uint32_t NewLevel, boolean_t AckRequired)
616 {
617 	ASSERT(sr == &test_sr);
618 	smb_oplock_ind_break(ofile, NewLevel, AckRequired, STATUS_CANT_GRANT);
619 }
620 
621 uint32_t
622 smb_oplock_wait_break(smb_node_t *node, int timeout)
623 {
624 	printf("*smb_oplock_wait_break (state=0x%x)\n",
625 	    node->n_oplock.ol_state);
626 	return (0);
627 }
628 
629 /*
630  * There are a couple DTRACE_PROBE* in smb_cmn_oplock.c but we're
631  * not linking with the user-level dtrace support, so just
632  * stub these out.
633  */
634 void
635 __dtrace_fksmb___probe1(char *n, unsigned long a)
636 {
637 }
638 void
639 __dtrace_fksmb___probe2(char *n, unsigned long a, unsigned long b)
640 {
641 }
642