xref: /netbsd-src/external/bsd/iscsi/dist/src/lib/disk.c (revision 3816d47b2c42fcd6e549e3407f842a5b1a1d23ad)
1 /* $NetBSD: disk.c,v 1.2 2009/06/30 02:44:52 agc Exp $ */
2 
3 /*-
4  * Copyright (c) 2006, 2007, 2008, 2009 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Alistair Crooks (agc@netbsd.org)
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 /*
32  * IMPORTANT:  READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
33  * By downloading, copying, installing or using the software you agree
34  * to this license.  If you do not agree to this license, do not
35  * download, install, copy or use the software.
36  *
37  * Intel License Agreement
38  *
39  * Copyright (c) 2000, Intel Corporation
40  * All rights reserved.
41  *
42  * Redistribution and use in source and binary forms, with or without
43  * modification, are permitted provided that the following conditions
44  * are met:
45  *
46  * -Redistributions of source code must retain the above copyright
47  *  notice, this list of conditions and the following disclaimer.
48  *
49  * -Redistributions in binary form must reproduce the above copyright
50  *  notice, this list of conditions and the following disclaimer in the
51  *  documentation and/or other materials provided with the
52  *  distribution.
53  *
54  * -The name of Intel Corporation may not be used to endorse or
55  *  promote products derived from this software without specific prior
56  *  written permission.
57  *
58  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
59  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
60  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
61  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL INTEL
62  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
63  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
64  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
65  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
66  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
67  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
68  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69  * SUCH DAMAGE.
70  */
71 #include "config.h"
72 
73 #ifdef HAVE_INTTYPES_H
74 #include <inttypes.h>
75 #endif
76 
77 #include <sys/types.h>
78 
79 #ifdef HAVE_SYS_PARAM_H
80 #include <sys/param.h>
81 #endif
82 
83 #ifdef HAVE_SYS_STAT_H
84 #include <sys/stat.h>
85 #endif
86 
87 #ifdef HAVE_SYS_UIO_H
88 #include <sys/uio.h>
89 #endif
90 
91 #ifdef HAVE_SYS_TIME_H
92 #include <sys/time.h>
93 #endif
94 
95 #ifdef HAVE_SYS_MMAN_H
96 #include <sys/mman.h>
97 #endif
98 
99 #ifdef HAVE_NETINET_IN_H
100 #include <netinet/in.h>
101 #endif
102 
103 #ifdef HAVE_ERRNO_H
104 #include <errno.h>
105 #endif
106 
107 #ifdef HAVE_FCNTL_H
108 #include <fcntl.h>
109 #endif
110 
111 #include <ctype.h>
112 #include <stdio.h>
113 #include <stdlib.h>
114 
115 #ifdef HAVE_STRING_H
116 #include <string.h>
117 #endif
118 
119 #include <unistd.h>
120 
121 #include "scsi_cmd_codes.h"
122 
123 #include "iscsiprotocol.h"
124 #include "compat.h"
125 #include "iscsiutil.h"
126 #include "device.h"
127 #include "target.h"
128 #include "defs.h"
129 #include "storage.h"
130 
131 #define iSCSI_DEFAULT_LUNS	1
132 #define iSCSI_DEFAULT_BLOCKLEN	512
133 
134 /* End disk configuration */
135 
136 /*
137  * Globals
138  */
139 enum {
140 	MAX_RESERVATIONS = 32,
141 
142 	ISCSI_FS =	0x03,
143 	ISCSI_CONTROL = 0x04
144 };
145 
146 #define MB(x)	((x) * 1024 * 1024)
147 
148 /* this struct describes an iscsi LUN */
149 typedef struct iscsi_disk_t {
150 	int		 type;		/* type of disk - fs/mmap and fs */
151 	char		 filename[MAXPATHLEN];	/* filename for the disk */
152 	uint8_t		*buffer;	/* buffer for disk read/write ops */
153 	uint64_t	 blockc;	/* # of blocks */
154 	uint64_t	 blocklen;	/* block size */
155 	uint64_t	 luns;		/* # of luns */
156 	uint64_t	 size;		/* size of complete disk */
157 	nbuuid_t	 uuid;		/* disk's uuid */
158 	char		*uuid_string;	/* uuid string */
159 	targv_t		*lunv;		/* the component devices and extents */
160 	uint32_t	 resc;		/* # of reservation keys */
161 	uint64_t	 reskeys[MAX_RESERVATIONS];	/* reservation keys */
162 } iscsi_disk_t;
163 
164 DEFINE_ARRAY(disks_t, iscsi_disk_t);
165 
166 static disks_t		disks;
167 static iscsi_disk_t	defaults;
168 
169 #ifndef FDATASYNC
170 /*
171 this means that we probably don't have the fsync_range(2) system call,
172 but no matter - define this here to preserve the abstraction for the
173 disk/extent code
174 */
175 #define FDATASYNC	0x0010
176 #endif
177 
178 /*
179  * Private Interface
180  */
181 static int      disk_read(target_session_t *, iscsi_scsi_cmd_args_t *,
182 				uint32_t, uint16_t, uint8_t);
183 static int      disk_write(target_session_t *, iscsi_scsi_cmd_args_t *,
184 				uint8_t, uint32_t, uint32_t);
185 
186 /* return the de index and offset within the device for RAID0 */
187 static int
188 raid0_getoff(disc_device_t *dp, uint64_t off, uint32_t *d, uint64_t *de_off)
189 {
190 	uint64_t	o;
191 
192 	for (o = 0, *d = 0 ; *d < dp->c ; o += dp->xv[*d].size, (*d)++) {
193 		if (off >= o && off < o + dp->xv[*d].size) {
194 			break;
195 		}
196 	}
197 	*de_off = off - o;
198 	return (*d < dp->c);
199 }
200 
201 /* open the extent's device */
202 static int
203 extent_open(disc_extent_t *xp, int mode, int flags)
204 {
205 	return xp->fd = open(xp->dev, mode, flags);
206 }
207 
208 /* (recursively) open the device's devices */
209 static int
210 device_open(disc_device_t *dp, int flags, int mode)
211 {
212 	int	fd;
213 	uint32_t i;
214 
215 	for (fd = -1, i = 0 ; i < dp->c ; i++) {
216 		switch (dp->xv[i].type) {
217 		case DE_DEVICE:
218 			fd = device_open(dp->xv[i].u.dp, flags, mode);
219 			if (fd < 0) {
220 				return -1;
221 			}
222 			break;
223 		case DE_EXTENT:
224 			fd = extent_open(dp->xv[i].u.xp, flags, mode);
225 			if (fd < 0) {
226 				return -1;
227 			}
228 			break;
229 		default:
230 			break;
231 		}
232 	}
233 	return fd;
234 }
235 
236 /* and for the undecided... */
237 static int
238 de_open(disc_de_t *dp, int flags, int mode)
239 {
240 	switch(dp->type) {
241 	case DE_DEVICE:
242 		return device_open(dp->u.dp, flags, mode);
243 	case DE_EXTENT:
244 		return extent_open(dp->u.xp, flags, mode);
245 	default:
246 		return -1;
247 	}
248 }
249 
250 /* lseek on the extent */
251 static off_t
252 extent_lseek(disc_extent_t *xp, off_t off, int whence)
253 {
254 	return lseek(xp->fd, (long long)(xp->sacred + off), whence);
255 }
256 
257 /* (recursively) lseek on the device's devices */
258 static off_t
259 device_lseek(disc_device_t *dp, off_t off, int whence)
260 {
261 	uint64_t	suboff;
262 	off_t		ret;
263 	uint32_t	d;
264 
265 	ret = -1;
266 	switch(dp->raid) {
267 	case 0:
268 		if (raid0_getoff(dp, (uint64_t) off, &d, &suboff)) {
269 			switch (dp->xv[d].type) {
270 			case DE_DEVICE:
271 				ret = device_lseek(dp->xv[d].u.dp,
272 					(off_t) suboff, whence);
273 				if (ret < 0) {
274 					return -1;
275 				}
276 				break;
277 			case DE_EXTENT:
278 				ret = extent_lseek(dp->xv[d].u.xp,
279 						(off_t) suboff, whence);
280 				if (ret < 0) {
281 					return -1;
282 				}
283 				break;
284 			default:
285 				break;
286 			}
287 		}
288 		break;
289 	case 1:
290 		for (d = 0 ; d < dp->c ; d++) {
291 			switch (dp->xv[d].type) {
292 			case DE_DEVICE:
293 				ret = device_lseek(dp->xv[d].u.dp, (off_t)off,
294 							whence);
295 				if (ret < 0) {
296 					return -1;
297 				}
298 				break;
299 			case DE_EXTENT:
300 				ret = extent_lseek(dp->xv[d].u.xp, (off_t)off,
301 							whence);
302 				if (ret < 0) {
303 					return -1;
304 				}
305 				break;
306 			default:
307 				break;
308 			}
309 		}
310 		break;
311 	default:
312 		break;
313 	}
314 	return dp->off = ret;
315 }
316 
317 /* and for the undecided... */
318 static off_t
319 de_lseek(disc_de_t *dp, off_t off, int whence)
320 {
321 	switch(dp->type) {
322 	case DE_DEVICE:
323 		return device_lseek(dp->u.dp, off, whence);
324 	case DE_EXTENT:
325 		return extent_lseek(dp->u.xp, off, whence);
326 	default:
327 		return -1;
328 	}
329 }
330 
331 /* fsync_range on the extent */
332 static int
333 extent_fsync_range(disc_extent_t *xp, int how, off_t from, off_t len)
334 {
335 #ifdef HAVE_FSYNC_RANGE
336 	return fsync_range(xp->fd, how, (off_t)(xp->sacred + from), len);
337 #else
338 	return fsync(xp->fd);
339 #endif
340 }
341 
342 /* (recursively) fsync_range on the device's devices */
343 static int
344 device_fsync_range(disc_device_t *dp, int how, off_t from, off_t len)
345 {
346 	uint64_t	suboff;
347 	int		ret;
348 	uint32_t	d;
349 
350 	ret = -1;
351 	switch(dp->raid) {
352 	case 0:
353 		if (raid0_getoff(dp, (uint64_t) from, &d, &suboff)) {
354 			switch (dp->xv[d].type) {
355 			case DE_DEVICE:
356 				ret = device_fsync_range(dp->xv[d].u.dp, how,
357 						(off_t)suboff, len);
358 				if (ret < 0) {
359 					return -1;
360 				}
361 				break;
362 			case DE_EXTENT:
363 				ret = extent_fsync_range(dp->xv[d].u.xp, how,
364 						(off_t)suboff, len);
365 				if (ret < 0) {
366 					return -1;
367 				}
368 				break;
369 			default:
370 				break;
371 			}
372 		}
373 		break;
374 	case 1:
375 		for (d = 0 ; d < dp->c ; d++) {
376 			switch (dp->xv[d].type) {
377 			case DE_DEVICE:
378 				ret = device_fsync_range(dp->xv[d].u.dp, how,
379 						from, len);
380 				if (ret < 0) {
381 					return -1;
382 				}
383 				break;
384 			case DE_EXTENT:
385 				ret = extent_fsync_range(dp->xv[d].u.xp, how,
386 						from, len);
387 				if (ret < 0) {
388 					return -1;
389 				}
390 				break;
391 			default:
392 				break;
393 			}
394 		}
395 		break;
396 	default:
397 		break;
398 	}
399 	dp->off = (uint64_t) ret;
400 	return ret;
401 }
402 
403 /* and for the undecided... */
404 static int
405 de_fsync_range(disc_de_t *dp, int how, off_t from, off_t len)
406 {
407 	switch(dp->type) {
408 	case DE_DEVICE:
409 		return device_fsync_range(dp->u.dp, how, from, len);
410 	case DE_EXTENT:
411 		return extent_fsync_range(dp->u.xp, how, from, len);
412 	default:
413 		return -1;
414 	}
415 }
416 
417 /* read from the extent */
418 static ssize_t
419 extent_read(disc_extent_t *xp, void *buf, size_t cc)
420 {
421 	return read(xp->fd, buf, cc);
422 }
423 
424 /* (recursively) read from the device's devices */
425 static ssize_t
426 device_read(disc_device_t *dp, void *buf, size_t cc)
427 {
428 	uint64_t	 suboff;
429 	uint64_t	 got;
430 	uint32_t	 d;
431 	ssize_t		 ret;
432 	size_t		 subcc;
433 	char		*cbuf;
434 
435 	ret = -1;
436 	switch(dp->raid) {
437 	case 0:
438 		for (cbuf = (char *) buf, got = 0 ; got < cc ; got += ret) {
439 			if (!raid0_getoff(dp, dp->off, &d, &suboff)) {
440 				return -1;
441 			}
442 			if (device_lseek(dp, (off_t)dp->off, SEEK_SET) < 0) {
443 				return -1;
444 			}
445 			subcc = MIN(cc - (size_t)got,
446 					(size_t)(dp->len - (size_t)dp->off));
447 			switch (dp->xv[d].type) {
448 			case DE_DEVICE:
449 				ret = device_read(dp->xv[d].u.dp,
450 						&cbuf[(int)got], subcc);
451 				if (ret < 0) {
452 					return -1;
453 				}
454 				break;
455 			case DE_EXTENT:
456 				ret = extent_read(dp->xv[d].u.xp,
457 						&cbuf[(int)got], subcc);
458 				if (ret < 0) {
459 					return -1;
460 				}
461 				break;
462 			default:
463 				break;
464 			}
465 			dp->off += ret;
466 		}
467 		ret = (ssize_t)got;
468 		break;
469 	case 1:
470 		for (d = 0 ; d < dp->c ; d++) {
471 			switch (dp->xv[d].type) {
472 			case DE_DEVICE:
473 				ret = device_read(dp->xv[d].u.dp, buf, cc);
474 				if (ret < 0) {
475 					return -1;
476 				}
477 				break;
478 			case DE_EXTENT:
479 				ret = extent_read(dp->xv[d].u.xp, buf, cc);
480 				if (ret < 0) {
481 					return -1;
482 				}
483 				break;
484 			default:
485 				break;
486 			}
487 		}
488 		dp->off += ret;
489 		break;
490 	default:
491 		break;
492 	}
493 	return ret;
494 }
495 
496 /* and for the undecided... */
497 static ssize_t
498 de_read(disc_de_t *dp, void *buf, size_t cc)
499 {
500 	switch(dp->type) {
501 	case DE_DEVICE:
502 		return device_read(dp->u.dp, buf, cc);
503 	case DE_EXTENT:
504 		return extent_read(dp->u.xp, buf, cc);
505 	default:
506 		return -1;
507 	}
508 }
509 
510 /* write to the extent */
511 static ssize_t
512 extent_write(disc_extent_t *xp, void *buf, size_t cc)
513 {
514 	return write(xp->fd, buf, cc);
515 }
516 
517 /* (recursively) write to the device's devices */
518 static ssize_t
519 device_write(disc_device_t *dp, void *buf, size_t cc)
520 {
521 	uint64_t	 suboff;
522 	uint64_t	 done;
523 	uint32_t	 d;
524 	ssize_t		 ret;
525 	size_t		 subcc;
526 	char		*cbuf;
527 
528 	ret = -1;
529 	switch(dp->raid) {
530 	case 0:
531 		for (cbuf = (char *) buf, done = 0 ; done < cc ; done += ret) {
532 			if (!raid0_getoff(dp, dp->off, &d, &suboff)) {
533 				return -1;
534 			}
535 			subcc = (size_t)MIN(cc - (size_t)done,
536 					(size_t)(dp->len - dp->off));
537 			if (device_lseek(dp, (off_t)dp->off, SEEK_SET) < 0) {
538 				return -1;
539 			}
540 			switch (dp->xv[d].type) {
541 			case DE_DEVICE:
542 				ret = device_write(dp->xv[d].u.dp,
543 					&cbuf[(int)done], subcc);
544 				if (ret < 0) {
545 					return -1;
546 				}
547 				break;
548 			case DE_EXTENT:
549 				ret = extent_write(dp->xv[d].u.xp,
550 						&cbuf[(int)done], subcc);
551 				if (ret < 0) {
552 					return -1;
553 				}
554 				break;
555 			default:
556 				break;
557 			}
558 			dp->off += ret;
559 		}
560 		ret = (ssize_t) done;
561 		break;
562 	case 1:
563 		for (d = 0 ; d < dp->c ; d++) {
564 			switch (dp->xv[d].type) {
565 			case DE_DEVICE:
566 				ret = device_write(dp->xv[d].u.dp, buf, cc);
567 				if (ret < 0) {
568 					iscsi_err(__FILE__, __LINE__,
569 						"device_write RAID1 device "
570 						"write failure\n");
571 					return -1;
572 				}
573 				break;
574 			case DE_EXTENT:
575 				ret = extent_write(dp->xv[d].u.xp, buf, cc);
576 				if (ret < 0) {
577 					iscsi_err(__FILE__, __LINE__,
578 						"device_write RAID1 extent "
579 						"write failure\n");
580 					return -1;
581 				}
582 				break;
583 			default:
584 				break;
585 			}
586 		}
587 		dp->off += ret;
588 		break;
589 	default:
590 		break;
591 	}
592 	return ret;
593 }
594 
595 /* and for the undecided... */
596 static ssize_t
597 de_write(disc_de_t *dp, void *buf, size_t cc)
598 {
599 	switch(dp->type) {
600 	case DE_DEVICE:
601 		return device_write(dp->u.dp, buf, cc);
602 	case DE_EXTENT:
603 		return extent_write(dp->u.xp, buf, cc);
604 	default:
605 		return -1;
606 	}
607 }
608 
609 /* return non-zero if the target is writable */
610 static int
611 target_writable(disc_target_t *tp)
612 {
613 	return !(tp->flags & TARGET_READONLY);
614 }
615 
616 /* return size of the extent */
617 static uint64_t
618 extent_getsize(disc_extent_t *xp)
619 {
620 	return xp->len;
621 }
622 
623 /* (recursively) return the size of the device's devices */
624 static uint64_t
625 device_getsize(disc_device_t *dp)
626 {
627 	uint64_t	size;
628 	uint32_t	d;
629 
630 	size = 0;
631 	switch(dp->raid) {
632 	case 0:
633 		for (d = 0 ; d < dp->c ; d++) {
634 			switch (dp->xv[d].type) {
635 			case DE_DEVICE:
636 				size += device_getsize(dp->xv[d].u.dp);
637 				break;
638 			case DE_EXTENT:
639 				size += extent_getsize(dp->xv[d].u.xp);
640 				break;
641 			default:
642 				break;
643 			}
644 		}
645 		break;
646 	case 1:
647 		size = dp->len;
648 		break;
649 	default:
650 		break;
651 	}
652 	return size;
653 }
654 
655 /* and for the undecided... */
656 static int64_t
657 de_getsize(disc_de_t *dp)
658 {
659 	switch(dp->type) {
660 	case DE_DEVICE:
661 		return device_getsize(dp->u.dp);
662 	case DE_EXTENT:
663 		return extent_getsize(dp->u.xp);
664 	default:
665 		return -1;
666 	}
667 }
668 
669 /* return a filename for the device or extent */
670 static char *
671 disc_get_filename(disc_de_t *de)
672 {
673 	switch (de->type) {
674 	case DE_EXTENT:
675 		return de->u.xp->dev;
676 	case DE_DEVICE:
677 		return disc_get_filename(&de->u.dp->xv[0]);
678 	default:
679 		return NULL;
680 	}
681 }
682 
683 /*
684  * Public Interface (called by utarget and ktarket)
685  */
686 
687  /* set various global variables */
688 void
689 device_set_var(const char *var, const char *arg)
690 {
691 	if (strcmp(var, "blocklen") == 0) {
692 		defaults.blocklen = strtoll(arg, (char **)NULL, 10);
693 	} else if (strcmp(var, "blocks") == 0) {
694 		defaults.blockc = strtoll(arg, (char **)NULL, 10);
695 	} else if (strcmp(var, "luns") == 0) {
696 		defaults.luns = strtoll(arg, (char **)NULL, 10);
697 	} else {
698 		(void) fprintf(stderr, "Unrecognised variable: `%s'\n", var);
699 	}
700 }
701 
702 /* allocate some space for a disk/extent, using an lseek, read and
703 * write combination */
704 static int
705 de_allocate(disc_de_t *de, char *filename)
706 {
707 	off_t	size;
708 	char	block[DEFAULT_TARGET_BLOCK_LEN];
709 
710 	size = de_getsize(de);
711 	if (de_lseek(de, size - sizeof(block), SEEK_SET) == -1) {
712 		iscsi_err(__FILE__, __LINE__,
713 				"error seeking \"%s\"\n", filename);
714 		return 0;
715 	}
716 	if (de_read(de, block, sizeof(block)) == -1) {
717 		iscsi_err(__FILE__, __LINE__,
718 				"error reading \"%s\"", filename);
719 		return 0;
720 	}
721 	if (de_write(de, block, sizeof(block)) == -1) {
722 		iscsi_err(__FILE__, __LINE__,
723 				"error writing \"%s\"", filename);
724 		return 0;
725 	}
726 	return 1;
727 }
728 
729 /* allocate space as desired */
730 static int
731 allocate_space(disc_target_t *tp)
732 {
733 	uint32_t	i;
734 
735 	/* Don't perform check for writability in the target here, as the
736 	following write() in de_allocate is non-destructive */
737 	switch(tp->de.type) {
738 	case DE_EXTENT:
739 		return de_allocate(&tp->de, tp->target);
740 	case DE_DEVICE:
741 		for (i = 0 ; i < tp->de.u.dp->c ; i++) {
742 			if (!de_allocate(&tp->de.u.dp->xv[i], tp->target)) {
743 				return 0;
744 			}
745 		}
746 		return 1;
747 	default:
748 		break;
749 	}
750 	return 0;
751 }
752 
753 /* copy src to dst, of size `n' bytes, padding any extra with `pad' */
754 static void
755 strpadcpy(uint8_t *dst, size_t dstlen, const char *src, const size_t srclen,
756 		char pad)
757 {
758 	if (srclen < dstlen) {
759 		(void) memcpy(dst, src, srclen);
760 		(void) memset(&dst[srclen], pad, dstlen - srclen);
761 	} else {
762 		(void) memcpy(dst, src, dstlen);
763 	}
764 }
765 
766 /* handle REPORT LUNs SCSI command */
767 static int
768 report_luns(uint64_t *data, int64_t luns)
769 {
770 	uint64_t	i;
771 	int32_t		off;
772 
773 	for (i = 0, off = 8 ; i < (uint64_t)luns ; i++, off += sizeof(i)) {
774 		data[(int)i] = ISCSI_HTONLL(i);
775 	}
776 	return off;
777 }
778 
779 /* handle persistent reserve in command */
780 static int
781 persistent_reserve_in(uint8_t action, uint8_t *data)
782 {
783 	uint64_t	key;
784 
785 	switch(action) {
786 	case PERSISTENT_RESERVE_IN_READ_KEYS:
787 		key = 0;	/* simulate "just powered on" */
788 		*((uint32_t *)(void *)data) =
789 				(uint32_t)ISCSI_HTONL((uint32_t) 0);
790 		*((uint32_t *) (void *)data + 4) =
791 				(uint32_t) ISCSI_HTONL((uint32_t) sizeof(key));
792 				/* length in bytes of list of keys */
793 		*((uint64_t *) (void *)data + 8) = (uint64_t) ISCSI_HTONLL(key);
794 		return 8 + sizeof(key);
795 	case PERSISTENT_RESERVE_IN_REPORT_CAPABILITIES:
796 		(void) memset(data, 0x0, 8);
797 		/* length is fixed at 8 bytes */
798 		*((uint16_t *)(void *)data) =
799 				(uint16_t)ISCSI_HTONS((uint16_t)8);
800 		data[2] = PERSISTENT_RESERVE_IN_CRH;
801 				/* also SIP_C, ATP_C and PTPL_C here */
802 		data[3] = 0;	/* also TMV and PTPL_A here */
803 		data[4] = 0;
804 			/* also WR_EX_AR, EX_AC_RD, WR_EX_RD, EX_AC, WR_EX */
805 		data[5] = 0;	/* also EX_AC_AR here */
806 		return 8;
807 	default:
808 		iscsi_err(__FILE__, __LINE__,
809 			"persistent_reserve_in: action %x unrecognised\n",
810 			action);
811 		return 0;
812 	}
813 }
814 
815 /* initialise the device */
816 int
817 device_init(iscsi_target_t *tgt, targv_t *tvp, disc_target_t *tp)
818 {
819 	iscsi_disk_t	*idisk;
820 	int	 	 mode;
821 
822 	ALLOC(iscsi_disk_t, disks.v, disks.size, disks.c, 10, 10,
823 			"device_init", ;);
824 	idisk = &disks.v[disks.c];
825 	idisk->lunv = tvp;
826 	if ((idisk->luns = defaults.luns) == 0) {
827 		idisk->luns = iSCSI_DEFAULT_LUNS;
828 	}
829 	idisk->blocklen = atoi(iscsi_target_getvar(tgt, "blocklen"));
830 	switch(idisk->blocklen) {
831 	case 512:
832 	case 1024:
833 	case 2048:
834 	case 4096:
835 	case 8192:
836 		break;
837 	default:
838 		iscsi_err(__FILE__, __LINE__,
839 			"Invalid block len %" PRIu64
840 			". Choose one of 512, 1024, 2048, 4096, or 8192.\n",
841 			idisk->blocklen);
842 		return -1;
843 	}
844 	idisk->size = de_getsize(&tp->de);
845 	idisk->blockc = idisk->size / idisk->blocklen;
846 	NEWARRAY(uint8_t, idisk->buffer, MB(1), "buffer1", ;);
847 	idisk->type = ISCSI_FS;
848 	printf("DISK: %" PRIu64 " logical unit%s (%" PRIu64 " blocks, %"
849 			PRIu64 " bytes/block), type %s\n",
850 			idisk->luns,
851 			(idisk->luns == 1) ? "" : "s",
852 			idisk->blockc, idisk->blocklen,
853 			(idisk->type == ISCSI_FS) ? "iscsi fs" :
854 				"iscsi fs mmap");
855 	printf("DISK: LUN 0: ");
856 	(void) strlcpy(idisk->filename, disc_get_filename(&tp->de),
857 			sizeof(idisk->filename));
858 	mode = (tp->flags & TARGET_READONLY) ? O_RDONLY : (O_CREAT | O_RDWR);
859 	if (de_open(&tp->de, mode, 0666) == -1) {
860 		iscsi_err(__FILE__, __LINE__,
861 			"error opening \"%s\"\n", idisk->filename);
862 		return -1;
863 	}
864 	if (!(tp->flags & TARGET_READONLY) && !allocate_space(tp)) {
865 		iscsi_err(__FILE__, __LINE__,
866 			"error allocating space for \"%s\"", tp->target);
867 		return -1;
868 	}
869 	printf("%" PRIu64 " MB %sdisk storage for \"%s\"\n",
870 		(de_getsize(&tp->de) / MB(1)),
871 		(tp->flags & TARGET_READONLY) ? "readonly " : "",
872 		tp->target);
873 	return disks.c++;
874 }
875 
876 static void
877 cdb2lba(uint32_t *lba, uint16_t *len, uint8_t *cdb)
878 {
879 	/* Some platforms (like strongarm) aligns on */
880 	/* word boundaries.  So HTONL and NTOHL won't */
881 	/* work here. */
882 	int	little_endian = 1;
883 
884 	if (*(char *) (void *) &little_endian) {
885 		/* little endian */
886 		((uint8_t *) (void *) lba)[0] = cdb[5];
887 		((uint8_t *) (void *) lba)[1] = cdb[4];
888 		((uint8_t *) (void *) lba)[2] = cdb[3];
889 		((uint8_t *) (void *) lba)[3] = cdb[2];
890 		((uint8_t *) (void *) len)[0] = cdb[8];
891 		((uint8_t *) (void *) len)[1] = cdb[7];
892 	} else {
893 		((uint8_t *) (void *) lba)[0] = cdb[2];
894 		((uint8_t *) (void *) lba)[1] = cdb[3];
895 		((uint8_t *) (void *) lba)[2] = cdb[4];
896 		((uint8_t *) (void *) lba)[3] = cdb[5];
897 		((uint8_t *) (void *) len)[0] = cdb[7];
898 		((uint8_t *) (void *) len)[1] = cdb[8];
899 	}
900 }
901 
902 /* handle MODE_SENSE_6 and MODE_SENSE_10 commands */
903 static int
904 mode_sense(const int bytes, target_cmd_t *cmd)
905 {
906 	iscsi_scsi_cmd_args_t	*args = cmd->scsi_cmd;
907 	uint16_t		 len;
908 	uint8_t			*cp;
909 	uint8_t			*cdb = args->cdb;
910 	size_t			 mode_data_len;
911 
912 	switch(bytes) {
913 	case 6:
914 		cp = args->send_data;
915 		len = ISCSI_MODE_SENSE_LEN;
916 		mode_data_len = len + 3;
917 
918 		iscsi_trace(TRACE_SCSI_CMD, "MODE_SENSE_6\n");
919 		(void) memset(cp, 0x0, mode_data_len);
920 
921 		cp[0] = mode_data_len;
922 		cp[1] = 0;
923 		cp[2] = 0;
924 		cp[3] = 8;	/* block descriptor length */
925 		cp[10] = 2;	/* density code and block length */
926 
927 		args->input = 1;
928 		args->length = (unsigned)len;
929 		args->status = SCSI_SUCCESS;
930 		return 1;
931 	case 10:
932 		cp = args->send_data;
933 		len = ISCSI_MODE_SENSE_LEN;
934 		mode_data_len = len + 3;
935 
936 		iscsi_trace(TRACE_SCSI_CMD, "MODE_SENSE_10\n");
937 		(void) memset(cp, 0x0, mode_data_len);
938 		if (cdb[4] == 0) {
939 			/* zero length cdb means just return success */
940 			args->input = 1;
941 			args->length = (unsigned)(mode_data_len);
942 			args->status = SCSI_SUCCESS;
943 			return 1;
944 		}
945 		if ((cdb[2] & PAGE_CONTROL_MASK) ==
946 					PAGE_CONTROL_CHANGEABLE_VALUES) {
947 			/* just send back a CHECK CONDITION */
948 			args->input = 1;
949 			args->length = (unsigned)(len);
950 			args->status = SCSI_CHECK_CONDITION;
951 			cp[2] = SCSI_SKEY_ILLEGAL_REQUEST;
952 			cp[12] = ASC_LUN_UNSUPPORTED;
953 			cp[13] = ASCQ_LUN_UNSUPPORTED;
954 			return 1;
955 		}
956 		iscsi_trace(TRACE_SCSI_CMD, "PC %02x\n", cdb[2]);
957 
958 		cp[0] = mode_data_len;
959 		cp[1] = 0;
960 		cp[2] = 0;
961 		cp[3] = 8;	/* block descriptor length */
962 		cp[10] = 2;	/* density code and block length */
963 
964 		args->input = 1;
965 		args->length = (unsigned)(len);
966 		args->status = SCSI_SUCCESS;
967 		return 1;
968 	}
969 	return 0;
970 }
971 
972 /* fill in the device serial number vital product data */
973 static uint8_t
974 serial_vpd(uint8_t *data)
975 {
976 	uint8_t	len;
977 
978 	data[0] = DISK_PERIPHERAL_DEVICE;
979 	data[1] = INQUIRY_DEVICE_IDENTIFICATION_VPD;
980 	len = 16;
981 	/* add target device's Unit Serial Number */
982 	/* section 7.6.10 of SPC-3 says that if there is no serial number,
983 	 * use spaces */
984 	strpadcpy(&data[4], (size_t)len, " ", strlen(" "), ' ');
985 	return len;
986 }
987 
988 /* fill in the device identification vital product data */
989 static void
990 device_vpd(iscsi_target_t *tgt, uint8_t *data, uint8_t *rspc,
991 		uint8_t *cdbsize, uint8_t lun, char *uuid)
992 {
993 	uint16_t	 len;
994 	uint8_t		*cp;
995 
996 	data[0] = DISK_PERIPHERAL_DEVICE;
997 	data[1] = INQUIRY_DEVICE_IDENTIFICATION_VPD;
998 	*rspc = 0;
999 	cp = &data[4];
1000 	/* add target device's IQN */
1001 	cp[0] = (INQUIRY_DEVICE_ISCSI_PROTOCOL << 4) |
1002 			INQUIRY_DEVICE_CODESET_UTF8;
1003 	cp[1] = (INQUIRY_DEVICE_PIV << 7) |
1004 			(INQUIRY_DEVICE_ASSOCIATION_TARGET_DEVICE << 4) |
1005 			INQUIRY_DEVICE_IDENTIFIER_SCSI_NAME;
1006 	len = (uint8_t) snprintf((char *)&cp[4],
1007 			(unsigned)(*cdbsize - (int)(cp - &data[4])), "%s",
1008 			iscsi_target_getvar(tgt, "iqn"));
1009 	cp[3] = len;
1010 	*rspc += len + 4;
1011 	cp += len + 4;
1012 	/* add target port's IQN + LUN */
1013 	cp[0] = (INQUIRY_DEVICE_ISCSI_PROTOCOL << 4) |
1014 		INQUIRY_DEVICE_CODESET_UTF8;
1015 	cp[1] = (INQUIRY_DEVICE_PIV << 7) |
1016 		(INQUIRY_DEVICE_ASSOCIATION_TARGET_PORT << 4) |
1017 		INQUIRY_DEVICE_IDENTIFIER_SCSI_NAME;
1018 	len = (uint8_t) snprintf((char *)&cp[4],
1019 				(unsigned)(*cdbsize - (int)(cp - &data[4])),
1020 				"%s,t,%#x",
1021 				iscsi_target_getvar(tgt, "iqn"),
1022 				lun);
1023 	cp[3] = len;
1024 	*rspc += len + 4;
1025 	cp += len + 4;
1026 	/* add target port's IQN + LUN extension */
1027 	cp[0] = (INQUIRY_DEVICE_ISCSI_PROTOCOL << 4) |
1028 		INQUIRY_DEVICE_CODESET_UTF8;
1029 	cp[1] = (INQUIRY_DEVICE_PIV << 7) |
1030 		(INQUIRY_DEVICE_ASSOCIATION_LOGICAL_UNIT << 4) |
1031 		INQUIRY_DEVICE_IDENTIFIER_SCSI_NAME;
1032 	len = (uint8_t) snprintf((char *)&cp[4],
1033 				(unsigned) (*cdbsize - (int)(cp - &data[4])),
1034 				"%s,L,0x%8.8s%4.4s%4.4s",
1035 				iscsi_target_getvar(tgt, "iqn"),
1036 				uuid, &uuid[9], &uuid[14]);
1037 	cp[3] = len;
1038 	*rspc += len + 4;
1039 	cp += len + 4;
1040 	/* add target's uuid as a T10 identifier */
1041 	cp[0] = (INQUIRY_DEVICE_ISCSI_PROTOCOL << 4) |
1042 		INQUIRY_DEVICE_CODESET_UTF8;
1043 	cp[1] = (INQUIRY_DEVICE_PIV << 7) |
1044 		(INQUIRY_DEVICE_ASSOCIATION_TARGET_DEVICE << 4) |
1045 		INQUIRY_IDENTIFIER_TYPE_T10;
1046 	strpadcpy(&cp[4], 8, ISCSI_VENDOR, strlen(ISCSI_VENDOR), ' ');
1047 	len = 8;
1048 	len += (uint8_t) snprintf((char *)&cp[8 + 4],
1049 				(unsigned)(*cdbsize - (int)(cp - &data[4])),
1050 				"0x%8.8s%4.4s%4.4s",
1051 				uuid, &uuid[9], &uuid[14]);
1052 	cp[3] = len;
1053 	*rspc += len + 4;
1054 }
1055 
1056 static void
1057 version_inquiry(uint8_t *data, uint8_t *cdbsize)
1058 {
1059 	char	versionstr[8];
1060 
1061 	data[0] = DISK_PERIPHERAL_DEVICE;
1062 	data[2] = SCSI_VERSION_SPC;
1063 	data[4] = *cdbsize - 4;	/* Additional length  */
1064 	data[7] |= (WIDE_BUS_32 | WIDE_BUS_16);
1065 	strpadcpy(&data[8], 8, ISCSI_VENDOR, strlen(ISCSI_VENDOR), ' ');
1066 	strpadcpy(&data[16], 16, ISCSI_PRODUCT, strlen(ISCSI_PRODUCT), ' ');
1067 	(void) snprintf(versionstr, sizeof(versionstr), "%d", ISCSI_VERSION);
1068 	strpadcpy(&data[32], 4, versionstr, strlen(versionstr), ' ');
1069 }
1070 
1071 int
1072 device_command(target_session_t *sess, target_cmd_t *cmd)
1073 {
1074 	iscsi_scsi_cmd_args_t  *args = cmd->scsi_cmd;
1075 	uint32_t        	status;
1076 	uint32_t		lba;
1077 	uint16_t		len;
1078 	uint8_t			*cdbsize;
1079 	uint8_t			*rspc;
1080 	uint8_t			*data;
1081 	uint8_t			*cdb;
1082 	uint8_t			lun;
1083 
1084 	cdb = args->cdb;
1085 	lun = (uint8_t) (args->lun >> 32);
1086 	cdbsize = &cdb[4];
1087 
1088 	/*
1089 	 * added section to return no device equivalent for lun request
1090 	 * beyond available lun
1091 	 */
1092 	if (lun >= disks.v[sess->d].luns) {
1093 		data = args->send_data;
1094 		(void) memset(data, 0x0, (size_t) *cdbsize);
1095 		/*
1096 		 * data[0] = 0x7F; means no device
1097 		 */
1098 		data[0] = 0x1F;	/* device type */
1099 		data[0] |= 0x60;/* peripheral qualifier */
1100 		args->input = 1;
1101 		args->length = cdb[4] + 1;
1102 		args->status = SCSI_SUCCESS;
1103 		return 0;
1104 	}
1105 
1106 	lun = (uint8_t) sess->d;
1107 	iscsi_trace(TRACE_SCSI_CMD, "SCSI op %#x (lun %d): \n", cdb[0], lun);
1108 
1109 	switch (cdb[0]) {
1110 	case TEST_UNIT_READY:
1111 		iscsi_trace(TRACE_SCSI_CMD, "TEST_UNIT_READY\n");
1112 		args->status = SCSI_SUCCESS;
1113 		args->length = 0;
1114 		break;
1115 
1116 	case INQUIRY:
1117 		iscsi_trace(TRACE_SCSI_CMD, "INQUIRY%s\n",
1118 				(cdb[1] & INQUIRY_EVPD_BIT) ?
1119 					" for Vital Product Data" : "");
1120 		data = args->send_data;
1121 		args->status = SCSI_SUCCESS;
1122 		/* Clear allocated buffer */
1123 		(void) memset(data, 0x0, (unsigned) *cdbsize);
1124 		if (cdb[1] & INQUIRY_EVPD_BIT) {
1125 			rspc = &data[3];
1126 			switch(cdb[2]) {
1127 			case INQUIRY_UNIT_SERIAL_NUMBER_VPD:
1128 				*rspc = serial_vpd(data);
1129 				args->length = 16;
1130 				break;
1131 			case INQUIRY_DEVICE_IDENTIFICATION_VPD:
1132 				if (disks.v[sess->d].uuid_string == NULL) {
1133 					nbuuid_create(&disks.v[sess->d].uuid,
1134 						&status);
1135 					nbuuid_to_string(&disks.v[sess->d].uuid,
1136 						&disks.v[sess->d].uuid_string,
1137 						&status);
1138 				}
1139 				device_vpd(sess->target, data, rspc, cdbsize,
1140 					lun, disks.v[sess->d].uuid_string);
1141 				args->length = *rspc + 6;
1142 				break;
1143 			case INQUIRY_SUPPORTED_VPD_PAGES:
1144 				data[0] = DISK_PERIPHERAL_DEVICE;
1145 				data[1] = INQUIRY_SUPPORTED_VPD_PAGES;
1146 				*rspc = 3;	/* # of supported pages */
1147 				data[4] = INQUIRY_SUPPORTED_VPD_PAGES;
1148 				data[5] = INQUIRY_DEVICE_IDENTIFICATION_VPD;
1149 				data[6] = EXTENDED_INQUIRY_DATA_VPD;
1150 				args->length = *cdbsize + 1;
1151 				break;
1152 			case EXTENDED_INQUIRY_DATA_VPD:
1153 				data[0] = DISK_PERIPHERAL_DEVICE;
1154 				data[1] = EXTENDED_INQUIRY_DATA_VPD;
1155 				data[3] = 0x3c;	/* length is defined to be 60 */
1156 				data[4] = 0;
1157 				data[5] = 0;
1158 				args->length = 64;
1159 				break;
1160 			default:
1161 				iscsi_err(__FILE__, __LINE__,
1162 					"Unsupported INQUIRY VPD page %x\n",
1163 					cdb[2]);
1164 				args->status = SCSI_CHECK_CONDITION;
1165 				break;
1166 			}
1167 		} else {
1168 			version_inquiry(data, cdbsize);
1169 			args->length = cdb[4] + 1;
1170 		}
1171 		if (args->status == SCSI_SUCCESS) {
1172 			args->input = 1;
1173 		}
1174 		break;
1175 
1176 	case MODE_SELECT_6:
1177 		iscsi_trace(TRACE_SCSI_CMD, "MODE_SELECT_6\n");
1178 		args->status = SCSI_SUCCESS;
1179 		args->length = 0;
1180 		break;
1181 
1182 	case STOP_START_UNIT:
1183 		iscsi_trace(TRACE_SCSI_CMD, "STOP_START_UNIT\n");
1184 		args->status = SCSI_SUCCESS;
1185 		args->length = 0;
1186 		break;
1187 
1188 	case READ_CAPACITY:
1189 		iscsi_trace(TRACE_SCSI_CMD, "READ_CAPACITY\n");
1190 		data = args->send_data;
1191 		*((uint32_t *)(void *)data) = (uint32_t) ISCSI_HTONL(
1192 				(uint32_t) disks.v[sess->d].blockc - 1);
1193 				/* Max LBA */
1194 		*((uint32_t *)(void *)(data + 4)) = (uint32_t) ISCSI_HTONL(
1195 				(uint32_t) disks.v[sess->d].blocklen);
1196 				/* Block len */
1197 		args->input = 8;
1198 		args->length = 8;
1199 		args->status = SCSI_SUCCESS;
1200 		break;
1201 
1202 	case WRITE_6:
1203 		lba = ISCSI_NTOHL(*((uint32_t *) (void *)cdb)) & 0x001fffff;
1204 		if ((len = *cdbsize) == 0) {
1205 			len = 256;
1206 		}
1207 		iscsi_trace(TRACE_SCSI_CMD,
1208 				"WRITE_6(lba %u, len %u blocks)\n", lba, len);
1209 		if (disk_write(sess, args, lun, lba, (unsigned) len) != 0) {
1210 			iscsi_err(__FILE__, __LINE__,
1211 				"disk_write() failed\n");
1212 			args->status = SCSI_CHECK_CONDITION;
1213 		}
1214 		args->length = 0;
1215 		break;
1216 
1217 
1218 	case READ_6:
1219 		lba = ISCSI_NTOHL(*((uint32_t *)(void *)cdb)) & 0x001fffff;
1220 		if ((len = *cdbsize) == 0) {
1221 			len = 256;
1222 		}
1223 		iscsi_trace(TRACE_SCSI_CMD,
1224 			"READ_6(lba %u, len %u blocks)\n", lba, len);
1225 		if (disk_read(sess, args, lba, len, lun) != 0) {
1226 			iscsi_err(__FILE__, __LINE__,
1227 					"disk_read() failed\n");
1228 			args->status = SCSI_CHECK_CONDITION;
1229 		}
1230 		args->input = 1;
1231 		break;
1232 
1233 	case MODE_SENSE_6:
1234 		mode_sense(6, cmd);
1235 		break;
1236 
1237 	case WRITE_10:
1238 	case WRITE_VERIFY:
1239 		cdb2lba(&lba, &len, cdb);
1240 
1241 		iscsi_trace(TRACE_SCSI_CMD,
1242 			"WRITE_10 | WRITE_VERIFY(lba %u, len %u blocks)\n",
1243 			lba, len);
1244 		if (disk_write(sess, args, lun, lba, (unsigned) len) != 0) {
1245 			iscsi_err(__FILE__, __LINE__,
1246 					"disk_write() failed\n");
1247 			args->status = SCSI_CHECK_CONDITION;
1248 		}
1249 		args->length = 0;
1250 		break;
1251 
1252 	case READ_10:
1253 		cdb2lba(&lba, &len, cdb);
1254 		iscsi_trace(TRACE_SCSI_CMD,
1255 				"READ_10(lba %u, len %u blocks)\n", lba, len);
1256 		if (disk_read(sess, args, lba, len, lun) != 0) {
1257 			iscsi_err(__FILE__, __LINE__,
1258 				"disk_read() failed\n");
1259 			args->status = SCSI_CHECK_CONDITION;
1260 		}
1261 		args->input = 1;
1262 		break;
1263 
1264 	case VERIFY:
1265 		/* For now just set the status to success. */
1266 		args->status = SCSI_SUCCESS;
1267 		break;
1268 
1269 	case SYNC_CACHE:
1270 		cdb2lba(&lba, &len, cdb);
1271 		iscsi_trace(TRACE_SCSI_CMD,
1272 			"SYNC_CACHE (lba %u, len %u blocks)\n", lba, len);
1273 		if (de_fsync_range(&disks.v[sess->d].lunv->v[lun].de,
1274 				FDATASYNC, lba,
1275 				(off_t)(len * disks.v[sess->d].blocklen)) < 0) {
1276 			iscsi_err(__FILE__, __LINE__,
1277 					"disk_read() failed\n");
1278 			args->status = SCSI_CHECK_CONDITION;
1279 		} else {
1280 			args->status = SCSI_SUCCESS;
1281 			args->length = 0;
1282 		}
1283 		break;
1284 
1285 	case LOG_SENSE:
1286 		iscsi_trace(TRACE_SCSI_CMD, "LOG_SENSE\n");
1287 		args->status = SCSI_SUCCESS;
1288 		args->length = 0;
1289 		break;
1290 
1291 	case MODE_SENSE_10:
1292 		mode_sense(10, cmd);
1293 		break;
1294 
1295 	case MODE_SELECT_10:
1296 		/* XXX still to do */
1297 		iscsi_trace(TRACE_SCSI_CMD, "MODE_SELECT_10\n");
1298 		args->status = SCSI_SUCCESS;
1299 		args->length = 0;
1300 		break;
1301 
1302 	case PERSISTENT_RESERVE_IN:
1303 		iscsi_trace(TRACE_SCSI_CMD, "PERSISTENT_RESERVE_IN\n");
1304 		args->length = persistent_reserve_in((cdb[1] &
1305 				PERSISTENT_RESERVE_IN_SERVICE_ACTION_MASK),
1306 				args->send_data);
1307 		args->status = SCSI_SUCCESS;
1308 		break;
1309 
1310 	case REPORT_LUNS:
1311 		iscsi_trace(TRACE_SCSI_CMD, "REPORT LUNS\n");
1312 		args->length = report_luns(
1313 				(uint64_t *)(void *)&args->send_data[8],
1314 				(off_t)disks.v[sess->d].luns);
1315 		*((uint32_t *)(void *)args->send_data) =
1316 				ISCSI_HTONL(disks.v[sess->d].luns *
1317 				sizeof(uint64_t));
1318 		args->input = 8;
1319 		args->status = SCSI_SUCCESS;
1320 		break;
1321 
1322 	case RESERVE_6:
1323 		iscsi_trace(TRACE_SCSI_CMD, "RESERVE_6\n");
1324 		args->status = SCSI_SUCCESS;
1325 		args->length = 0;
1326 		break;
1327 
1328 	case RELEASE_6:
1329 		iscsi_trace(TRACE_SCSI_CMD, "RELEASE_6\n");
1330 		args->status = SCSI_SUCCESS;
1331 		args->length = 0;
1332 		break;
1333 
1334 	case RESERVE_10:
1335 		iscsi_trace(TRACE_SCSI_CMD, "RESERVE_10\n");
1336 		args->status = SCSI_SUCCESS;
1337 		args->length = 0;
1338 		break;
1339 
1340 	case RELEASE_10:
1341 		iscsi_trace(TRACE_SCSI_CMD, "RELEASE_10\n");
1342 		args->status = SCSI_SUCCESS;
1343 		args->length = 0;
1344 		break;
1345 
1346 	default:
1347 		iscsi_err(__FILE__, __LINE__,
1348 			"UNKNOWN OPCODE %#x\n", cdb[0]);
1349 		/* to not cause confusion with some initiators */
1350 		args->status = SCSI_CHECK_CONDITION;
1351 		break;
1352 	}
1353 	iscsi_trace(TRACE_SCSI_DEBUG,
1354 		"SCSI op %#x: done (status %#x)\n", cdb[0], args->status);
1355 	return 0;
1356 }
1357 
1358 int
1359 device_shutdown(target_session_t *sess)
1360 {
1361 	USE_ARG(sess);
1362 	return 1;
1363 }
1364 
1365 /*
1366  * Private Interface
1367  */
1368 
1369 static int
1370 disk_write(target_session_t *sess, iscsi_scsi_cmd_args_t *args, uint8_t lun,
1371 		uint32_t lba, uint32_t len)
1372 {
1373 	struct iovec    sg;
1374 	uint64_t        byte_offset;
1375 	uint64_t        bytec;
1376 	uint8_t        *ptr;
1377 
1378 	byte_offset = lba * disks.v[sess->d].blocklen;
1379 	bytec = len * disks.v[sess->d].blocklen;
1380 	ptr = NULL;
1381 	iscsi_trace(TRACE_SCSI_DATA,
1382 		"writing %" PRIu64
1383 		" bytes from socket into device at byte offset %" PRIu64 "\n",
1384 		bytec, byte_offset);
1385 
1386 	if ((unsigned) bytec > MB(1)) {
1387 		iscsi_err(__FILE__, __LINE__, "bytec > %u\n", bytec);
1388 		NO_CLEANUP;
1389 		return -1;
1390 	}
1391 
1392 	/* Assign ptr for write data */
1393 	ptr = disks.v[sess->d].buffer;
1394 
1395 	/* Have target do data transfer */
1396 	sg.iov_base = ptr;
1397 	sg.iov_len = (unsigned)bytec;
1398 	if (target_transfer_data(sess, args, &sg, 1) != 0) {
1399 		iscsi_err(__FILE__, __LINE__,
1400 			"target_transfer_data() failed\n");
1401 	}
1402 	/* Finish up write */
1403 	if (de_lseek(&disks.v[sess->d].lunv->v[lun].de, (off_t)byte_offset,
1404 				SEEK_SET) == -1) {
1405 		iscsi_err(__FILE__, __LINE__,
1406 			"lseek() to offset %" PRIu64 " failed\n",
1407 			byte_offset);
1408 		return -1;
1409 	}
1410 	if (!target_writable(&disks.v[sess->d].lunv->v[lun])) {
1411 		iscsi_err(__FILE__, __LINE__,
1412 			"write() of %" PRIu64 " bytes failed at offset %"
1413 			PRIu64 ", size %" PRIu64 "[READONLY TARGET]\n",
1414 			bytec, byte_offset,
1415 			de_getsize(&disks.v[sess->d].lunv->v[lun].de));
1416 		return -1;
1417 	}
1418 	if ((uint64_t)de_write(&disks.v[sess->d].lunv->v[lun].de, ptr,
1419 			(unsigned) bytec) != bytec) {
1420 		iscsi_err(__FILE__, __LINE__,
1421 			"write() of %" PRIu64 " bytes failed at offset %"
1422 			PRIu64 ", size %" PRIu64 "\n",
1423 			bytec, byte_offset,
1424 			de_getsize(&disks.v[sess->d].lunv->v[lun].de));
1425 		return -1;
1426 	}
1427 	iscsi_trace(TRACE_SCSI_DATA,
1428 		"wrote %" PRIu64 " bytes to device OK\n", bytec);
1429 	return 0;
1430 }
1431 
1432 static int
1433 disk_read(target_session_t *sess, iscsi_scsi_cmd_args_t *args, uint32_t lba,
1434 		uint16_t len, uint8_t lun)
1435 {
1436 	uint64_t        byte_offset;
1437 	uint64_t        bytec;
1438 	uint64_t        extra;
1439 	uint8_t        *ptr;
1440 	uint32_t        n;
1441 	int             rc;
1442 
1443 	byte_offset = lba * disks.v[sess->d].blocklen;
1444 	bytec = len * disks.v[sess->d].blocklen;
1445 	extra = 0;
1446 	ptr = NULL;
1447 	if (len == 0) {
1448 		iscsi_err(__FILE__, __LINE__, "Zero \"len\"\n");
1449 		NO_CLEANUP;
1450 		return -1;
1451 	}
1452 	if (lba > disks.v[sess->d].blockc - 1 ||
1453 	    (lba + len) > disks.v[sess->d].blockc) {
1454 		iscsi_err(__FILE__, __LINE__,
1455 			"attempt to read beyond end of media\n"
1456 			"max_lba = %" PRIu64 ", requested lba = %u, len = %u\n",
1457 			disks.v[sess->d].blockc - 1, lba, len);
1458 		return -1;
1459 	}
1460 	if ((unsigned) bytec > MB(1)) {
1461 		iscsi_err(__FILE__, __LINE__, "bytec > %u\n", bytec);
1462 		NO_CLEANUP;
1463 		return -1;
1464 	}
1465 	ptr = disks.v[sess->d].buffer;
1466 	n = 0;
1467 	do {
1468 		if (de_lseek(&disks.v[sess->d].lunv->v[lun].de,
1469 				(off_t)(n + byte_offset), SEEK_SET) == -1) {
1470 			iscsi_err(__FILE__, __LINE__, "lseek failed\n");
1471 			return -1;
1472 		}
1473 		rc = de_read(&disks.v[sess->d].lunv->v[lun].de, ptr + n,
1474 				(size_t)(bytec - n));
1475 		if (rc <= 0) {
1476 			iscsi_err(__FILE__, __LINE__,
1477 				"read failed: rc %d errno %d\n", rc, errno);
1478 			return -1;
1479 		}
1480 		n += rc;
1481 		if (n < bytec) {
1482 			iscsi_err(__FILE__, __LINE__,
1483 				"Got partial file read: %d bytes of %" PRIu64
1484 				"\n", rc, bytec - n + rc);
1485 		}
1486 	} while (n < bytec);
1487 	((struct iovec *)(void *)args->send_data)[0].iov_base =
1488 			ptr + (unsigned) extra;
1489 	((struct iovec *)(void *)args->send_data)[0].iov_len =
1490 			(unsigned) bytec;
1491 	args->length = (unsigned) bytec;
1492 	args->send_sg_len = 1;
1493 	args->status = 0;
1494 	return 0;
1495 }
1496