1/*
2 * IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. By downloading, copying, installing or
3 * using the software you agree to this license. If you do not agree to this license, do not download, install,
4 * copy or use the software.
5 *
6 * Intel License Agreement
7 *
8 * Copyright (c) 2000, Intel Corporation
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that
12 * the following conditions are met:
13 *
14 * -Redistributions of source code must retain the above copyright notice, this list of conditions and the
15 *  following disclaimer.
16 *
17 * -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
18 *  following disclaimer in the documentation and/or other materials provided with the distribution.
19 *
20 * -The name of Intel Corporation may not be used to endorse or promote products derived from this software
21 *  without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
24 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
26 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32#include "config.h"
33
34#include <sys/types.h>
35
36#ifdef HAVE_NETINET_IN_H
37#include <netinet/in.h>
38#endif
39
40#ifdef HAVE_SYS_MMAN_H
41#include <sys/mman.h>
42#endif
43
44#ifdef HAVE_SYS_UIO_H
45#include <sys/uio.h>
46#endif
47
48#ifdef HAVE_SYS_TIME_H
49#include <sys/time.h>
50#endif
51
52#ifdef HAVE_SYS_STAT_H
53#include <sys/stat.h>
54#endif
55
56#ifdef HAVE_SYS_VFS_H
57#include <sys/vfs.h>
58#endif
59
60#include <stdio.h>
61#include <stdlib.h>
62
63#ifdef HAVE_STRING_H
64#include <string.h>
65#endif
66
67#include <unistd.h>
68
69#ifdef HAVE_ERRNO_H
70#include <errno.h>
71#endif
72
73#include <unistd.h>
74
75#ifdef HAVE_FCNTL_H
76#include <fcntl.h>
77#endif
78
79#ifdef HAVE_UTIME_H
80#include <utime.h>
81#endif
82
83#include "scsi_cmd_codes.h"
84
85#include "iscsi.h"
86#include "iscsiutil.h"
87#include "device.h"
88#include "osd.h"
89
90/*
91 * Globals
92 */
93
94static int      osd_luns = CONFIG_OSD_LUNS_DFLT;
95static uint64_t osd_capacity = CONFIG_OSD_CAPACITY_DFLT * 1048576;
96static char     base_dir[64] = CONFIG_OSD_BASEDIR_DFLT;
97
98#ifndef __KERNEL__
99void
100device_set_var(const char *var, char *arg)
101{
102	if (strcmp(var, "capacity") == 0) {
103		osd_capacity = strtoll(arg, (char **) NULL, 10) * 1048576;
104	} else if (strcmp(var, "luns") == 0) {
105		osd_luns = atoi(arg);
106	} else if (strcmp(var, "directory") == 0) {
107		(void) strlcpy(base_dir, arg, sizeof(base_dir));
108	} else {
109		(void) fprintf(stderr, "Unrecognised variable: `%s'\n", var);
110	}
111}
112#endif
113
114int
115device_init(globals_t *gp, char *dev)
116{
117	struct stat     st;
118	char            FileName[1024];
119	int             i;
120
121	if (stat(base_dir, &st) < 0) {
122
123		/* Create directory for OSD */
124
125		if (mkdir(base_dir, 0755) != 0) {
126			if (errno != EEXIST) {
127				iscsi_err(__FILE__, __LINE__, "error creating directory \"%s\" for OSD: errno %d\n", base_dir, errno);
128				return -1;
129			}
130		}
131		/* Create directory for LU */
132
133		for (i = 0; i < osd_luns; i++) {
134			sprintf(FileName, "%s/lun_%d", base_dir, i);
135			if (mkdir(FileName, 0755) != 0) {
136				if (errno != EEXIST) {
137					iscsi_err(__FILE__, __LINE__, "error creating \"%s\" for LU %d: errno %d\n", FileName, i, errno);
138					return -1;
139				}
140			}
141		}
142	}
143	/* Display LU info */
144
145	return 0;
146}
147
148int
149osd_read_callback(void *arg)
150{
151	struct iovec   *sg = (struct iovec *) arg;
152	int             i = 0;
153
154	while (sg[i].iov_base != NULL) {
155		iscsi_free_atomic(sg[i].iov_base);
156		i++;
157	}
158	return 0;
159}
160
161int
162device_command(target_session_t * sess, target_cmd_t * cmd)
163{
164	iscsi_scsi_cmd_args_t *args = cmd->scsi_cmd;
165	uint8_t  *data;
166	char            FileName[1024];
167	uint8_t        *write_data = NULL;
168	uint8_t        *read_data = NULL;
169	uint8_t        *set_list = NULL;
170	uint8_t        *get_list = NULL;
171	struct iovec    sg[3];
172	int             sg_len = 0;
173	int             rc;
174	osd_args_t      osd_args;
175	uint32_t        GroupID = 0;
176	uint64_t        UserID = 0;
177	char            string[1024];
178	uint8_t  *get_data = NULL;
179	uint32_t        page = 0;
180	uint32_t        index = 0;
181	int             attr_len = 0;
182
183	iscsi_trace(TRACE_SCSI_CMD, "SCSI op 0x%x (lun %llu)\n", args->cdb[0], args->lun);
184
185	if (args->lun >= osd_luns) {
186		iscsi_trace(TRACE_SCSI_DEBUG, "invalid lun: %llu\n", args->lun);
187		args->status = 0x01;
188		return 0;
189	}
190	args->status = 1;
191
192	switch (args->cdb[0]) {
193
194	case TEST_UNIT_READY:
195
196		iscsi_trace(TRACE_SCSI_CMD, "TEST_UNIT_READY(lun %llu)\n", args->lun);
197		args->status = 0;
198		args->length = 0;
199		break;
200
201	case INQUIRY:
202
203		iscsi_trace(TRACE_SCSI_CMD, "INQUIRY(lun %llu)\n", args->lun);
204		data = args->send_data;
205		memset(data, 0, args->cdb[4]);	/* Clear allocated buffer */
206		data[0] = 0x0e;	/* Peripheral Device Type */
207		/* data[1] |= 0x80;                        // Removable Bit */
208		data[2] |= 0x02;/* ANSI-approved version */
209		/* data[3] |= 0x80;                        // AENC */
210		/* data[3] |= 0x40;                        // TrmIOP */
211		/* data[3] |= 0x20;                        // NormACA */
212		data[4] = args->cdb[4] - 4;	/* Additional length */
213		/*
214		 * data[7] |= 0x80;                        // Relative
215		 * addressing
216		 */
217		data[7] |= 0x40;/* WBus32 */
218		data[7] |= 0x20;/* WBus16 */
219		/* data[7] |= 0x10;                        // Sync */
220		/* data[7] |= 0x08;                        // Linked Commands */
221		/* data[7] |= 0x04;                        // TransDis */
222		/*
223		 * data[7] |= 0x02;                        // Tagged Command
224		 * Queueing
225		 */
226		/* data[7] |= 0x01;                        // SftRe */
227		(void) memset(data + 8, 0x0, 32);
228		strlcpy(data + 8, OSD_VENDOR, 8);	/* Vendor */
229		strlcpy(data + 16, OSD_PRODUCT, 16);	/* Product ID */
230		(void) snprintf(data + 32, 8, "%d", OSD_VERSION);	/* Product Revision */
231		args->input = 1;
232		args->length = args->cdb[4] + 1;
233		args->status = 0;
234
235		break;
236
237	case 0x7F:
238
239		OSD_DECAP_CDB(args->cdb, args->ext_cdb, &osd_args);
240		/* OSD_PRINT_CDB(args->cdb, args->ext_cdb); */
241		GroupID = osd_args.GroupID;
242		UserID = osd_args.UserID;
243
244		/*
245	         * Transfer all data
246	         */
247
248		if (osd_args.set_attributes_list_length) {
249			if ((set_list = iscsi_malloc_atomic(osd_args.set_attributes_list_length)) == NULL) {
250				iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
251				goto done;
252			}
253			sg[sg_len].iov_base = set_list;
254			sg[sg_len].iov_len = osd_args.set_attributes_list_length;
255			sg_len++;
256		}
257		if (osd_args.get_attributes_list_length) {
258			if ((get_list = iscsi_malloc_atomic(osd_args.get_attributes_list_length)) == NULL) {
259				iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
260				goto done;
261			}
262			sg[sg_len].iov_base = get_list;
263			sg[sg_len].iov_len = osd_args.get_attributes_list_length;
264			sg_len++;
265		}
266		if (osd_args.service_action == OSD_WRITE) {
267			if ((write_data = iscsi_malloc_atomic(osd_args.length)) == NULL) {
268				iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
269				goto done;
270			}
271			sg[sg_len].iov_base = write_data;
272			sg[sg_len].iov_len = osd_args.length;
273			sg_len++;
274		}
275		if (sg_len) {
276			if (target_transfer_data(sess, args, sg, sg_len) != 0) {
277				iscsi_err(__FILE__, __LINE__, "target_transfer_data() failed\n");
278				goto done;
279			}
280		}
281		/*
282	         * Set any attributes
283	         */
284
285		if (osd_args.set_attributes_list_length) {
286			uint32_t        page, attr;
287			uint16_t        len;
288			int             i;
289
290			iscsi_trace(TRACE_OSD, "OSD_SET_ATTR(lun %llu, GroupID 0x%x, UserID 0x%llx)\n", args->lun, osd_args.GroupID, osd_args.UserID);
291			for (i = 0; i < osd_args.set_attributes_list_length;) {
292				page = ISCSI_NTOHL(*((uint32_t *) (&(set_list[i]))));
293				i += 4;
294				attr = ISCSI_NTOHL(*((uint32_t *) (&(set_list[i]))));
295				i += 4;
296				len = ISCSI_NTOHS(*((uint16_t *) (&(set_list[i]))));
297				i += 2;
298				sprintf(FileName, "%s/lun_%llu/0x%x/0x%llx.0x%x.%u",
299					base_dir, args->lun, osd_args.GroupID, osd_args.UserID, page, attr);
300				if ((rc = open(FileName, O_WRONLY | O_CREAT, 0644)) == -1) {
301					iscsi_err(__FILE__, __LINE__, "error opening \"%s\": errno %d\n", FileName, errno);
302					goto done;
303				}
304				if (write(rc, set_list + i, len) != len) {
305					iscsi_err(__FILE__, __LINE__, "write() failed\n");
306				}
307				close(rc);
308				i += len;
309				iscsi_trace(TRACE_OSD, "SET(0x%x,%u,%u>\n", page, attr, len);
310			}
311		}
312		args->send_sg_len = 0;
313		sg_len = 0;
314
315		switch (osd_args.service_action) {
316
317		case OSD_CREATE_GROUP:
318
319			do {
320				GroupID = rand() % 1048576 * 1024 + 1;
321				sprintf(FileName, "%s/lun_%llu/0x%x", base_dir, args->lun, GroupID);
322				rc = mkdir(FileName, 0755);
323			} while (rc == -1 && errno == EEXIST);
324			iscsi_trace(TRACE_OSD, "OSD_CREATE_GROUP(lun %llu) --> 0x%x\n", args->lun, GroupID);
325			args->status = 0;
326			break;
327
328		case OSD_REMOVE_GROUP:
329
330			iscsi_trace(TRACE_OSD, "OSD_REMOVE_GROUP(lun %llu, 0x%x)\n", args->lun, osd_args.GroupID);
331			sprintf(FileName, "%s/lun_%llu/0x%x", base_dir, args->lun, osd_args.GroupID);
332			if ((rc = rmdir(FileName)) == -1) {
333				iscsi_err(__FILE__, __LINE__, "rmdir(\"%s\") failed: errno %d\n", FileName, errno);
334				goto done;
335			}
336			args->status = 0;
337			break;
338
339		case OSD_CREATE:
340
341			UserID = rand() % 1048576 * 1024 + 1;
342create_user_again:
343			sprintf(FileName, "%s/lun_%llu/0x%x/0x%llx",
344			     base_dir, args->lun, osd_args.GroupID, UserID);
345			rc = open(FileName, O_CREAT | O_EXCL | O_RDWR, 0644);
346			if ((rc == -1) && (errno == EEXIST)) {
347				UserID = rand() % 1048576 * 1024 + 1;
348				goto create_user_again;
349			}
350			close(rc);
351			iscsi_trace(TRACE_OSD, "OSD_CREATE(lun %llu, GroupID 0x%x) --> 0x%llx\n", args->lun, osd_args.GroupID, UserID);
352			args->status = 0;
353
354			break;
355
356		case OSD_REMOVE:
357
358			iscsi_trace(TRACE_OSD, "OSD_REMOVE(lun %llu, 0x%llx)\n", args->lun, osd_args.UserID);
359			sprintf(FileName, "%s/lun_%llu/0x%x/0x%llx",
360				base_dir, args->lun, osd_args.GroupID, osd_args.UserID);
361			if ((rc = unlink(FileName)) == -1) {
362				iscsi_err(__FILE__, __LINE__, "unlink(\"%s\") failed: errno %d\n", FileName, errno);
363				goto done;
364			}
365			sprintf(string, "rm -f %s/lun_%llu/0x%x/0x%llx.*", base_dir, args->lun, osd_args.GroupID, osd_args.UserID);
366			if (system(string) != 0) {
367				iscsi_err(__FILE__, __LINE__, "\"%s\" failed\n", string);
368				return -1;
369			}
370			args->status = 0;
371			break;
372
373		case OSD_WRITE:
374			iscsi_trace(TRACE_OSD, "OSD_WRITE(lun %llu, GroupID 0x%x, UserID 0x%llx, length %llu, offset %llu)\n",
375			      args->lun, osd_args.GroupID, osd_args.UserID, osd_args.length, osd_args.offset);
376			sprintf(FileName, "%s/lun_%llu/0x%x/0x%llx",
377				base_dir, args->lun, osd_args.GroupID, osd_args.UserID);
378			if ((rc = open(FileName, O_WRONLY, 0644)) == -1) {
379				iscsi_err(__FILE__, __LINE__, "error opening \"%s\": errno %d\n", FileName, errno);
380				goto write_done;
381			}
382			if (lseek(rc, osd_args.offset, SEEK_SET) == -1) {
383				iscsi_err(__FILE__, __LINE__, "error seeking \"%s\": errno %d\n", FileName, errno);
384				goto write_done;
385			}
386			if (write(rc, write_data, osd_args.length) != osd_args.length) {
387				iscsi_err(__FILE__, __LINE__, "write() failed\n");
388				goto write_done;
389			}
390			close(rc);
391			args->status = 0;
392write_done:
393			break;
394
395		case OSD_READ:
396			iscsi_trace(TRACE_OSD, "OSD_READ(lun %llu, GroupID 0x%x, UserID 0x%llx, length %llu, offset %llu)\n",
397			      args->lun, osd_args.GroupID, osd_args.UserID, osd_args.length, osd_args.offset);
398			sprintf(FileName, "%s/lun_%llu/0x%x/0x%llx",
399				base_dir, args->lun, osd_args.GroupID, osd_args.UserID);
400			if ((rc = open(FileName, O_RDONLY, 0644)) == -1) {
401				iscsi_err(__FILE__, __LINE__, "error opening \"%s\": errno %d\n", FileName, errno);
402				goto read_done;
403			}
404			if ((read_data = iscsi_malloc_atomic(osd_args.length)) == NULL) {
405				iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
406				goto read_done;
407			}
408			if (lseek(rc, osd_args.offset, SEEK_SET) == -1) {
409				iscsi_err(__FILE__, __LINE__, "error seeking \"%s\": errno %d\n", FileName, errno);
410				goto read_done;
411			}
412			if (read(rc, read_data, osd_args.length) != osd_args.length) {
413				iscsi_err(__FILE__, __LINE__, "read() failed\n");
414				goto read_done;
415			}
416			close(rc);
417			args->status = 0;
418read_done:
419			if (args->status == 0) {
420				args->input = 1;
421				sg[0].iov_base = read_data;
422				sg[0].iov_len = osd_args.length;
423				sg[1].iov_base = NULL;
424				sg[1].iov_len = 0;
425				args->send_data = (void *) sg;
426				args->send_sg_len = 1;
427				sg_len++;
428				cmd->callback = osd_read_callback;
429				cmd->callback_arg = sg;
430			} else {
431				if (read_data)
432					iscsi_free_atomic(read_data);
433				args->length = 0;	/* Need a better way of
434							 * specifying an error.. */
435			}
436			break;
437
438		case OSD_GET_ATTR:
439			iscsi_trace(TRACE_OSD, "OSD_GET_ATTR(lun %llu, GroupID 0x%x, UserID 0x%llx)\n",
440			      args->lun, osd_args.GroupID, osd_args.UserID);
441			args->status = 0;
442			break;
443
444		case OSD_SET_ATTR:
445			args->status = 0;
446			break;
447		}
448
449		if (args->status)
450			goto done;
451
452		/*
453	         * Send back requested attributes
454	         */
455
456		if (osd_args.get_attributes_list_length || osd_args.get_attributes_page) {
457			if ((get_data = iscsi_malloc_atomic(osd_args.get_attributes_allocation_length)) == NULL) {
458				iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
459				goto done;
460			}
461		}
462		if (osd_args.get_attributes_list_length) {
463			int             i;
464
465			for (i = 0; i < osd_args.get_attributes_list_length;) {
466				page = ISCSI_NTOHL(*((uint32_t *) (&(get_list[i]))));
467				i += 4;
468				index = ISCSI_NTOHL(*((uint32_t *) (&(get_list[i]))));
469				i += 4;
470				iscsi_trace(TRACE_OSD, "GET(0x%x,%u)\n", page, index);
471
472				switch (page) {
473				case 0x40000001:
474					switch (index) {
475					case 0x1:
476						*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(page);
477						attr_len += 4;
478						*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(index);
479						attr_len += 4;
480						*((uint16_t *) & get_data[attr_len]) = ISCSI_HTONS(4);
481						attr_len += 2;
482						*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(GroupID);
483						attr_len += 4;
484						break;
485					default:
486						iscsi_err(__FILE__, __LINE__, "unknown attr index %u\n", index);
487						goto done;
488					}
489					break;
490				case 0x00000001:
491					switch (index) {
492					case 0x1:
493						*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(page);
494						attr_len += 4;
495						*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(index);
496						attr_len += 4;
497						*((uint16_t *) & get_data[attr_len]) = ISCSI_HTONS(4);
498						attr_len += 2;
499						*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(GroupID);
500						attr_len += 4;
501						break;
502					case 0x2:
503						*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(page);
504						attr_len += 4;
505						*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(index);
506						attr_len += 4;
507						*((uint16_t *) & get_data[attr_len]) = ISCSI_HTONS(8);
508						attr_len += 2;
509						*((uint64_t *) & get_data[attr_len]) = ISCSI_HTONLL(UserID);
510						attr_len += 8;
511						break;
512					default:
513						iscsi_err(__FILE__, __LINE__, "unknown attr index %u\n", index);
514						goto done;
515					}
516					break;
517
518					/* Vendor-specific */
519
520				case 0x30000000:
521					switch (index) {
522					case 0x1:
523						*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(page);
524						attr_len += 4;
525						*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(index);
526						attr_len += 4;
527						*((uint16_t *) & get_data[attr_len]) = ISCSI_HTONS(480);
528						attr_len += 2;
529						sprintf(FileName, "%s/lun_%llu/0x%x/0x%llx.0x%x.%u",
530							base_dir, args->lun, osd_args.GroupID, osd_args.UserID, page, index);
531						if ((rc = open(FileName, O_RDONLY, 0644)) == -1) {
532							iscsi_err(__FILE__, __LINE__, "error opening \"%s\": errno %d\n", FileName, errno);
533						}
534						if (read(rc, get_data + attr_len, 480) != 480) {
535							iscsi_err(__FILE__, __LINE__, "read() failed\n");
536							goto done;
537						}
538						close(rc);
539						attr_len += 480;
540						break;
541					default:
542						iscsi_err(__FILE__, __LINE__, "unknown vendor attr index %u\n", index);
543						goto done;
544					}
545					break;
546
547				default:
548					iscsi_err(__FILE__, __LINE__, "unknown page 0x%x\n", page);
549					goto done;
550				}
551			}
552		}
553		if (osd_args.get_attributes_page) {
554
555			/*
556			 * Right now, if we get a request for an entire page,
557			 * we return only one attribute.
558			 */
559
560			page = osd_args.get_attributes_page;
561
562			switch (osd_args.get_attributes_page) {
563			case 0x40000001:
564				index = 1;
565				*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(page);
566				attr_len += 4;
567				*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(index);
568				attr_len += 4;
569				*((uint16_t *) & get_data[attr_len]) = ISCSI_HTONS(4);
570				attr_len += 2;
571				*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(GroupID);
572				attr_len += 4;
573				break;
574
575			case 0x00000001:
576				index = 2;
577				*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(page);
578				attr_len += 4;
579				*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(index);
580				attr_len += 4;
581				*((uint16_t *) & get_data[attr_len]) = ISCSI_HTONS(8);
582				attr_len += 2;
583				*((uint64_t *) & get_data[attr_len]) = ISCSI_HTONLL(UserID);
584				attr_len += 8;
585				break;
586
587			case 0x30000000:
588				index = 1;
589				*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(page);
590				attr_len += 4;
591				*((uint32_t *) & get_data[attr_len]) = ISCSI_HTONL(index);
592				attr_len += 4;
593				*((uint16_t *) & get_data[attr_len]) = ISCSI_HTONS(480);
594				attr_len += 2;
595				sprintf(FileName, "%s/lun_%llu/0x%x/0x%llx.0x%x.%u",
596					base_dir, args->lun, osd_args.GroupID, osd_args.UserID, page, index);
597				if ((rc = open(FileName, O_RDONLY, 0644)) == -1) {
598					iscsi_err(__FILE__, __LINE__, "error opening \"%s\": errno %d\n", FileName, errno);
599				}
600				if (read(rc, get_data + attr_len, 480) != 480) {
601					iscsi_err(__FILE__, __LINE__, "read() failed\n");
602					goto done;
603				}
604				close(rc);
605				attr_len += 480;
606				break;
607			default:
608				iscsi_err(__FILE__, __LINE__, "page not yet supported\n");
609				goto done;
610			}
611		}
612		if (attr_len) {
613			if (attr_len != osd_args.get_attributes_allocation_length) {
614				iscsi_err(__FILE__, __LINE__, "allocation lengths differ: got %u, expected %u\n",
615					    osd_args.get_attributes_allocation_length, attr_len);
616				goto done;
617			}
618			if (!args->status) {
619				args->input = 1;
620				sg[sg_len].iov_base = get_data;
621				sg[sg_len].iov_len = osd_args.get_attributes_allocation_length;
622				sg_len++;
623				sg[sg_len].iov_base = NULL;
624				sg[sg_len].iov_len = 0;
625				args->send_data = (void *) sg;
626				args->send_sg_len++;
627				cmd->callback = osd_read_callback;
628				cmd->callback_arg = sg;
629			} else {
630				if (get_data)
631					iscsi_free_atomic(get_data);
632			}
633		}
634		break;
635
636	default:
637		iscsi_err(__FILE__, __LINE__, "UNKNOWN OPCODE 0x%x\n", args->cdb[0]);
638		args->status = 0x01;
639		break;
640	}
641
642
643done:
644	iscsi_trace(TRACE_SCSI_DEBUG, "SCSI op 0x%x: done (status 0x%x)\n", args->cdb[0], args->status);
645	if (set_list) {
646		iscsi_free_atomic(set_list);
647	}
648	if (get_list) {
649		iscsi_free_atomic(get_list);
650	}
651	if (write_data) {
652		iscsi_free_atomic(write_data);
653	}
654	return 0;
655}
656
657/* ARGSUSED */
658int
659device_shutdown(target_session_t *sess)
660{
661	return 0;
662}
663