Sync usage with man page.
[netbsd-mini2440.git] / external / bsd / iscsi / dist / src / initiator / iscsi-initiator.c
blob01fb7da696d15c347e60c5a7d4e17ce746e9b817
1 /*-
2 * Copyright (c) 2007,2009 The NetBSD Foundation, Inc.
3 * All rights reserved.
5 * This code is derived from software contributed to The NetBSD Foundation
6 * by Alistair Crooks (agc@netbsd.org)
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
29 #include <sys/types.h>
31 #define FUSE_USE_VERSION 26
33 #include <err.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <fuse.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
42 #include "scsi_cmd_codes.h"
43 #include "iscsi.h"
44 #include "initiator.h"
45 #include "tests.h"
47 #include "virtdir.h"
49 #if defined(__NetBSD__) && defined(USE_LIBKMOD)
50 #include "libkmod.h"
51 #endif
53 #include "defs.h"
55 static int verbose; /* how chatty are we? */
57 static virtdir_t iscsi;
59 enum {
60 VendorLen = 8,
61 ProductLen = 16,
62 VersionLen = 4,
64 SGsize = 131072
68 /* this struct keeps information on the target */
69 typedef struct targetinfo_t {
70 char *host; /* resolvable host name */
71 char *ip; /* textual IP address */
72 char *targetname; /* name of iSCSI target prog */
73 char *stargetname; /* short name of the target */
74 uint64_t target; /* target number */
75 uint32_t lun; /* LUN number */
76 uint32_t lbac; /* number of LBAs */
77 uint32_t blocksize; /* size of device blocks */
78 uint32_t devicetype; /* SCSI device type */
79 char vendor[VendorLen + 1];
80 /* device vendor information */
81 char product[ProductLen + 1];
82 /* device product information */
83 char version[VersionLen + 1];
84 /* device version information */
85 char *serial; /* unit serial number */
86 } targetinfo_t;
88 DEFINE_ARRAY(targetv_t, targetinfo_t);
90 static targetv_t tv; /* target vector of targetinfo_t structs */
92 /* iqns and target addresses are returned as pairs in this dynamic array */
93 static strv_t all_targets;
95 /* Small Target Info... */
96 typedef struct sti_t {
97 struct stat st; /* normal stat info */
98 uint64_t target; /* cached target number, so we don't have an expensive pathname-based lookup */
99 } sti_t;
101 #ifndef __UNCONST
102 #define __UNCONST(x) (x)
103 #endif
105 static void
106 lba2cdb(uint8_t *cdb, uint32_t *lba, uint16_t *len)
108 /* Some platforms (like strongarm) aligns on */
109 /* word boundaries. So HTONL and NTOHL won't */
110 /* work here. */
111 int little_endian = 1;
113 if (*(char *) (void *) &little_endian) {
114 /* little endian */
115 cdb[2] = ((uint8_t *) (void *)lba)[3];
116 cdb[3] = ((uint8_t *) (void *)lba)[2];
117 cdb[4] = ((uint8_t *) (void *)lba)[1];
118 cdb[5] = ((uint8_t *) (void *)lba)[0];
119 cdb[7] = ((uint8_t *) (void *)len)[1];
120 cdb[8] = ((uint8_t *) (void *)len)[0];
121 } else {
122 /* big endian */
123 cdb[2] = ((uint8_t *) (void *)lba)[2];
124 cdb[3] = ((uint8_t *) (void *)lba)[3];
125 cdb[4] = ((uint8_t *) (void *)lba)[0];
126 cdb[5] = ((uint8_t *) (void *)lba)[1];
127 cdb[7] = ((uint8_t *) (void *)len)[0];
128 cdb[8] = ((uint8_t *) (void *)len)[1];
132 /* read the capacity (maximum LBA and blocksize) from the target */
133 int
134 read_capacity(uint64_t target, uint32_t lun, uint32_t *maxlba, uint32_t *blocklen)
136 iscsi_scsi_cmd_args_t args;
137 initiator_cmd_t cmd;
138 uint8_t data[8];
139 uint8_t cdb[16];
141 (void) memset(cdb, 0x0, sizeof(cdb));
142 cdb[0] = READ_CAPACITY;
143 cdb[1] = lun << 5;
145 (void) memset(&args, 0x0, sizeof(args));
146 args.recv_data = data;
147 args.input = 1;
148 args.lun = lun;
149 args.trans_len = 8;
150 args.cdb = cdb;
152 (void) memset(&cmd, 0, sizeof(initiator_cmd_t));
154 cmd.isid = target;
155 cmd.type = ISCSI_SCSI_CMD;
156 cmd.ptr = &args;
158 if (initiator_command(&cmd) != 0) {
159 iscsi_err(__FILE__, __LINE__, "initiator_command() failed\n");
160 return -1;
162 if (args.status) {
163 iscsi_err(__FILE__, __LINE__, "READ_CAPACITY failed (status %#x)\n", args.status);
164 return -1;
166 *maxlba = ISCSI_NTOHL(*((uint32_t *) (data)));
167 *blocklen = ISCSI_NTOHL(*((uint32_t *) (data + 4)));
168 if (*maxlba == 0) {
169 iscsi_err(__FILE__, __LINE__, "Device returned Maximum LBA of zero\n");
170 return -1;
172 if (*blocklen % 2) {
173 iscsi_err(__FILE__, __LINE__, "Device returned strange block len: %u\n", *blocklen);
174 return -1;
176 return 0;
179 /* send inquiry command to the target, to get it to identify itself */
180 static int
181 inquiry(uint64_t target, uint32_t lun, uint8_t type, uint8_t inquire, uint8_t *data)
183 iscsi_scsi_cmd_args_t args;
184 initiator_cmd_t cmd;
185 uint8_t cdb[16];
187 (void) memset(cdb, 0x0, sizeof(cdb));
188 cdb[0] = INQUIRY;
189 cdb[1] = type | (lun << 5);
190 cdb[2] = inquire;
191 cdb[4] = 256 - 1;
193 (void) memset(&args, 0x0, sizeof(args));
194 args.input = 1;
195 args.trans_len = 256;
196 args.cdb = cdb;
197 args.lun = lun;
198 args.recv_data = data;
199 (void) memset(&cmd, 0x0, sizeof(cmd));
200 cmd.isid = target;
201 cmd.type = ISCSI_SCSI_CMD;
202 cmd.ptr = &args;
204 if (initiator_command(&cmd) != 0) {
205 iscsi_err(__FILE__, __LINE__, "initiator_command() failed\n");
206 return -1;
208 if (args.status) {
209 iscsi_err(__FILE__, __LINE__, "INQUIRY failed (status %#x)\n", args.status);
210 return -1;
213 return 0;
216 /* read or write a single block of information */
217 static int
218 blockop(uint64_t target, uint32_t lun, uint32_t lba, uint32_t len,
219 uint32_t blocklen, uint8_t *data, int writing)
221 iscsi_scsi_cmd_args_t args;
222 initiator_cmd_t cmd;
223 uint16_t readlen;
224 uint8_t cdb[16];
226 /* Build CDB */
227 (void) memset(cdb, 0, 16);
228 cdb[0] = (writing) ? WRITE_10 : READ_10;
229 cdb[1] = lun << 5;
230 readlen = (uint16_t) len;
231 lba2cdb(cdb, &lba, &readlen);
233 /* Build SCSI command */
234 (void) memset(&args, 0x0, sizeof(args));
235 if (writing) {
236 args.send_data = data;
237 args.output = 1;
238 } else {
239 args.recv_data = data;
240 args.input = 1;
242 args.lun = lun;
243 args.trans_len = len*blocklen;
244 args.length = len*blocklen;
245 args.cdb = cdb;
246 (void) memset(&cmd, 0, sizeof(initiator_cmd_t));
247 cmd.isid = target;
248 cmd.type = ISCSI_SCSI_CMD;
249 cmd.ptr = &args;
250 /* Execute iSCSI command */
251 if (initiator_command(&cmd) != 0) {
252 iscsi_err(__FILE__, __LINE__, "initiator_command() failed\n");
253 return -1;
256 if (args.status) {
257 iscsi_err(__FILE__, __LINE__, "scsi_command() failed (status %#x)\n", args.status);
258 return -1;
260 return 0;
263 /* perform a scatter/gather block operation */
264 static int
265 sgblockop(uint64_t target, uint32_t lun, uint32_t lba, uint32_t len,
266 uint32_t blocklen, uint8_t *data, int sglen, int writing)
268 iscsi_scsi_cmd_args_t args;
269 initiator_cmd_t cmd;
270 uint16_t readlen;
271 uint8_t cdb[16];
273 /* Build CDB */
275 (void) memset(cdb, 0, 16);
276 cdb[0] = (writing) ? WRITE_10 : READ_10;
277 cdb[1] = lun << 5;
278 readlen = (uint16_t) len;
279 lba2cdb(cdb, &lba, &readlen);
281 /* Build iSCSI command */
282 (void) memset(&args, 0x0, sizeof(args));
283 args.lun = lun;
284 args.output = (writing) ? 1 : 0;
285 args.input = (writing) ? 0 : 1;
286 args.trans_len = len * blocklen;
287 args.length = len * blocklen;
288 args.send_data = (writing) ? data : NULL;
289 args.send_sg_len = (writing) ? sglen : 0;
290 args.recv_data = (writing) ? NULL : data;
291 args.recv_sg_len = (writing) ? 0 : sglen;
292 args.cdb = cdb;
293 memset(&cmd, 0, sizeof(initiator_cmd_t));
294 cmd.isid = target;
295 cmd.ptr = &args;
296 cmd.type = ISCSI_SCSI_CMD;
298 /* Execute iSCSI command */
300 if (initiator_command(&cmd) != 0) {
301 iscsi_err(__FILE__, __LINE__, "initiator_command() failed\n");
302 return -1;
304 if (args.status) {
305 iscsi_err(__FILE__, __LINE__, "scsi_command() failed (status %#x)\n", args.status);
306 return -1;
308 return 0;
311 /* read info from the target - method depends on size of data being read */
312 static int
313 targetop(uint32_t t, uint64_t offset, uint32_t length, uint32_t request, char *buf, int writing)
315 struct iovec *iov;
316 uint32_t ioc;
317 uint32_t i;
318 int req_len;
320 if (request > SGsize) {
321 /* split up request into blocksize chunks */
322 ioc = request / SGsize;
323 if ((ioc * SGsize) < request)
324 ioc++;
325 if ((iov = iscsi_malloc(ioc * sizeof(*iov))) == NULL) {
326 iscsi_err(__FILE__, __LINE__, "out of memory\n");
327 return -1;
330 for (i = 0 ; i < ioc ; i++) {
331 iov[i].iov_base = &buf[i * SGsize];
332 if (i == (ioc - 1)) { /* last one */
333 iov[i].iov_len = request - (i * SGsize);
334 } else {
335 iov[i].iov_len = SGsize;
339 if (sgblockop(tv.v[t].target, tv.v[t].lun, offset / tv.v[t].blocksize, (length / tv.v[t].blocksize), tv.v[t].blocksize, (uint8_t *) iov, ioc, writing) != 0) {
340 iscsi_free(iov);
341 iscsi_err(__FILE__, __LINE__, "read_10() failed\n");
342 return -1;
344 iscsi_free(iov);
345 } else {
346 req_len = length / tv.v[t].blocksize;
347 if ((req_len * tv.v[t].blocksize) < length)
348 req_len++;
349 if (blockop(tv.v[t].target, tv.v[t].lun, offset / tv.v[t].blocksize,
350 req_len, tv.v[t].blocksize, (uint8_t *) buf, writing) != 0) {
351 iscsi_err(__FILE__, __LINE__, "read_10() failed\n");
352 return -1;
355 return 0;
359 /****************************************************************************/
361 /* perform the stat operation */
362 /* if this is the root, then just synthesise the data */
363 /* otherwise, retrieve the data, and be sure to fill in the size */
364 static int
365 iscsifs_getattr(const char *path, struct stat *st)
367 virt_dirent_t *ep;
368 sti_t *p;
370 if (strcmp(path, "/") == 0) {
371 (void) memset(st, 0x0, sizeof(*st));
372 st->st_mode = S_IFDIR | 0755;
373 st->st_nlink = 2;
374 return 0;
376 if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) {
377 return -ENOENT;
379 switch(ep->type) {
380 case 'b':
381 (void) memcpy(st, &iscsi.file, sizeof(*st));
382 st->st_mode = (S_IFBLK | 0644);
383 break;
384 case 'c':
385 (void) memcpy(st, &iscsi.file, sizeof(*st));
386 st->st_mode = (S_IFCHR | 0644);
387 break;
388 case 'd':
389 (void) memcpy(st, &iscsi.dir, sizeof(*st));
390 break;
391 case 'f':
392 (void) memcpy(st, &iscsi.file, sizeof(*st));
393 p = (sti_t *) ep->tgt;
394 st->st_size = p->st.st_size;
395 break;
396 case 'l':
397 (void) memcpy(st, &iscsi.lnk, sizeof(*st));
398 st->st_size = ep->tgtlen;
399 break;
400 default:
401 warn("unknown directory type `%c'", ep->type);
402 return -ENOENT;
404 st->st_ino = ep->ino;
405 return 0;
408 /* readdir operation */
409 static int
410 iscsifs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
411 off_t offset, struct fuse_file_info * fi)
413 virt_dirent_t *dp;
414 VIRTDIR *dirp;
416 if ((dirp = openvirtdir(&iscsi, path)) == NULL) {
417 return 0;
419 filler(buf, ".", NULL, 0);
420 filler(buf, "..", NULL, 0);
421 while ((dp = readvirtdir(dirp)) != NULL) {
422 filler(buf, dp->d_name, NULL, 0);
424 closevirtdir(dirp);
425 return 0;
428 /* open the file in the file system */
429 static int
430 iscsifs_open(const char *path, struct fuse_file_info *fi)
432 virt_dirent_t *ep;
433 const char *slash;
435 if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) {
436 return -ENOENT;
438 /* check path is the correct one */
439 if ((slash = strrchr(path, '/')) == NULL) {
440 slash = path;
441 } else {
442 slash += 1;
444 if (strcmp(slash, "storage") != 0) {
445 return -ENOENT;
447 return 0;
450 /* read the storage from the iSCSI target */
451 static int
452 iscsifs_read(const char *path, char *buf, size_t size, off_t offset,
453 struct fuse_file_info * fi)
455 virt_dirent_t *ep;
456 uint64_t target;
457 sti_t *p;
459 if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) {
460 return -ENOENT;
463 p = (sti_t *)ep->tgt;
464 target = p->target;
466 if (targetop(target, offset, size, size, buf, 0) < 0) {
467 return -EPERM;
469 return size;
472 /* write the file's contents to the file system */
473 static int
474 iscsifs_write(const char *path, const char *buf, size_t size, off_t offset,
475 struct fuse_file_info * fi)
477 virt_dirent_t *ep;
478 uint64_t target;
479 sti_t *p;
481 if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) {
482 return -ENOENT;
485 p = (sti_t *)ep->tgt;
486 target = p->target;
488 if (targetop(target, offset, size, size, __UNCONST(buf), 1) < 0) {
489 return -EPERM;
491 return size;
494 /* fill in the statvfs struct */
495 static int
496 iscsifs_statfs(const char *path, struct statvfs *st)
498 (void) memset(st, 0x0, sizeof(*st));
499 return 0;
502 /* read the symbolic link */
503 static int
504 iscsifs_readlink(const char *path, char *buf, size_t size)
506 virt_dirent_t *ep;
508 if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) {
509 return -ENOENT;
511 if (ep->tgt == NULL) {
512 return -ENOENT;
514 (void) strlcpy(buf, ep->tgt, size);
515 return 0;
518 /* operations struct */
519 static struct fuse_operations iscsiops = {
520 .getattr = iscsifs_getattr,
521 .readlink = iscsifs_readlink,
522 .readdir = iscsifs_readdir,
523 .open = iscsifs_open,
524 .read = iscsifs_read,
525 .write = iscsifs_write,
526 .statfs = iscsifs_statfs
529 int
530 main(int argc, char **argv)
532 iscsi_initiator_t ini;
533 initiator_target_t tinfo;
534 unsigned u;
535 uint32_t lbac;
536 uint32_t blocksize;
537 uint8_t data[256];
538 sti_t sti;
539 char hostname[1024];
540 char name[1024];
541 char *colon;
542 char *host;
543 char *user;
544 char buf[32];
545 char devtype;
546 int discover;
547 int cc;
548 int i;
550 (void) memset(&tinfo, 0x0, sizeof(tinfo));
551 iscsi_initiator_set_defaults(&ini);
552 user = NULL;
553 (void) gethostname(host = hostname, sizeof(hostname));
554 discover = 0;
555 (void) stat("/etc/hosts", &sti.st);
556 devtype = 'f';
557 iscsi_initiator_setvar(&ini, "address family", "4");
558 while ((i = getopt(argc, argv, "46a:bcd:Dfh:p:t:u:v:V")) != -1) {
559 switch(i) {
560 case '4':
561 case '6':
562 buf[0] = i;
563 buf[1] = 0x0;
564 iscsi_initiator_setvar(&ini, "address family", buf);
565 break;
566 case 'a':
567 iscsi_initiator_setvar(&ini, "auth type", optarg);
568 break;
569 case 'b':
570 devtype = 'b';
571 break;
572 case 'c':
573 devtype = 'c';
574 break;
575 case 'd':
576 iscsi_initiator_setvar(&ini, "digest type", optarg);
577 break;
578 case 'D':
579 discover = 1;
580 break;
581 case 'f':
582 devtype = 'f';
583 break;
584 case 'h':
585 iscsi_initiator_setvar(&ini, "target hostname", optarg);
586 break;
587 case 'p':
588 iscsi_initiator_setvar(&ini, "target port", optarg);
589 break;
590 case 't':
591 iscsi_initiator_setvar(&ini, "target instance", optarg);
592 break;
593 case 'u':
594 iscsi_initiator_setvar(&ini, "user", optarg);
595 break;
596 case 'V':
597 (void) printf("\"%s\" %s\nPlease send all bug reports "
598 "to %s\n",
599 PACKAGE_NAME,
600 PACKAGE_VERSION,
601 PACKAGE_BUGREPORT);
602 exit(EXIT_SUCCESS);
603 /* NOTREACHED */
604 case 'v':
605 verbose += 1;
606 if (strcmp(optarg, "net") == 0) {
607 iscsi_initiator_setvar(&ini, "debug", "net");
608 } else if (strcmp(optarg, "iscsi") == 0) {
609 iscsi_initiator_setvar(&ini, "debug", "iscsi");
610 } else if (strcmp(optarg, "scsi") == 0) {
611 iscsi_initiator_setvar(&ini, "debug", "scsi");
612 } else if (strcmp(optarg, "all") == 0) {
613 iscsi_initiator_setvar(&ini, "debug", "all");
615 break;
616 default:
617 (void) fprintf(stderr, "%s: unknown option `%c'",
618 *argv, i);
621 if (iscsi_initiator_getvar(&ini, "user") == NULL) {
622 iscsi_err(__FILE__, __LINE__, "user must be specified with -u\n");
623 exit(EXIT_FAILURE);
626 if (iscsi_initiator_start(&ini) == -1) {
627 iscsi_err(__FILE__, __LINE__, "initiator_init() failed\n");
628 exit(EXIT_FAILURE);
631 if (iscsi_initiator_discover(host, 0, 0) < 0) {
632 printf("initiator_discover() in discover failed\n");
633 exit(EXIT_FAILURE);
636 if (iscsi_initiator_get_targets(0,&all_targets) == -1) {
637 iscsi_err(__FILE__, __LINE__,
638 "initiator_get_targets() failed\n");
639 exit(EXIT_FAILURE);
643 if (discover) {
644 printf("Targets available from host %s:\n",host);
645 for (u = 0; u < all_targets.c ; u += 2) {
646 printf("%s at %s\n", all_targets.v[u],
647 all_targets.v[u + 1]);
650 exit(EXIT_SUCCESS);
653 if (all_targets.c/2 > CONFIG_INITIATOR_NUM_TARGETS) {
654 (void) fprintf(stderr,
655 "CONFIG_INITIATOR_NUM_TARGETS in initiator.h "
656 "is too small. %d targets available, "
657 "only %d configurable.\n",
658 all_targets.c/2, CONFIG_INITIATOR_NUM_TARGETS);
659 (void) fprintf(stderr,
660 "Truncating number of targets to %d.\n",
661 CONFIG_INITIATOR_NUM_TARGETS);
662 all_targets.c = CONFIG_INITIATOR_NUM_TARGETS;
665 sti.st.st_ino = 0x15c51;
667 #if defined(__NetBSD__) && defined(USE_LIBKMOD)
668 /* check that the puffs module is loaded on NetBSD */
669 if (kmodstat("puffs", NULL) == 0 && !kmodload("puffs")) {
670 (void) fprintf(stderr, "initiator: can't load puffs module\n");
672 #endif
674 for (u = 0 ; u < all_targets.c / 2 ; u++) {
675 ALLOC(targetinfo_t, tv.v, tv.size, tv.c, 10, 10, "iscsifs",
676 exit(EXIT_FAILURE));
678 initiator_set_target_name(u, all_targets.v[u * 2]);
680 if (iscsi_initiator_discover(host, u, 0) < 0) {
681 printf("iscsi_initiator_discover() failed\n");
682 break;
685 get_target_info(u, &tinfo);
686 if ((colon = strrchr(tinfo.TargetName, ':')) == NULL) {
687 colon = tinfo.TargetName;
688 } else {
689 colon += 1;
692 /* stuff size into st.st_size */
693 (void) read_capacity(u, 0, &lbac, &blocksize);
694 sti.st.st_size = ((uint64_t)lbac + 1) * blocksize;
695 sti.target = u;
697 tv.v[tv.c].host = strdup(tinfo.name);
698 tv.v[tv.c].ip = strdup(tinfo.ip);
699 tv.v[tv.c].targetname = strdup(tinfo.TargetName);
700 tv.v[tv.c].stargetname = strdup(colon);
701 tv.v[tv.c].target = u;
702 tv.v[tv.c].lun = 0;
703 tv.v[tv.c].lbac = lbac;
704 tv.v[tv.c].blocksize = blocksize;
706 /* get iSCSI target information */
707 (void) memset(data, 0x0, sizeof(data));
708 inquiry(u, 0, 0, 0, data);
709 tv.v[tv.c].devicetype = (data[0] & 0x1f);
710 (void) memcpy(tv.v[tv.c].vendor, &data[8], VendorLen);
711 (void) memcpy(tv.v[tv.c].product, &data[8 + VendorLen],
712 ProductLen);
713 (void) memcpy(tv.v[tv.c].version,
714 &data[8 + VendorLen + ProductLen], VersionLen);
715 (void) memset(data, 0x0, sizeof(data));
716 inquiry(u, 0, INQUIRY_EVPD_BIT, INQUIRY_UNIT_SERIAL_NUMBER_VPD,
717 data);
718 tv.v[tv.c].serial = strdup((char *)&data[4]);
720 /* create the tree using virtdir routines */
721 cc = snprintf(name, sizeof(name), "/%s/%s", host, colon);
722 virtdir_add(&iscsi, name, cc, 'd', name, cc);
723 cc = snprintf(name, sizeof(name), "/%s/%s/storage", host,
724 colon);
725 virtdir_add(&iscsi, name, cc, devtype, (void *)&sti,
726 sizeof(sti));
727 cc = snprintf(name, sizeof(name), "/%s/%s/hostname", host,
728 colon);
729 virtdir_add(&iscsi, name, cc, 'l', tinfo.name,
730 strlen(tinfo.name));
731 cc = snprintf(name, sizeof(name), "/%s/%s/ip", host, colon);
732 virtdir_add(&iscsi, name, cc, 'l', tinfo.ip, strlen(tinfo.ip));
733 cc = snprintf(name, sizeof(name), "/%s/%s/targetname", host,
734 colon);
735 virtdir_add(&iscsi, name, cc, 'l', tinfo.TargetName,
736 strlen(tinfo.TargetName));
737 cc = snprintf(name, sizeof(name), "/%s/%s/vendor", host, colon);
738 virtdir_add(&iscsi, name, cc, 'l', tv.v[tv.c].vendor,
739 strlen(tv.v[tv.c].vendor));
740 cc = snprintf(name, sizeof(name), "/%s/%s/product", host,
741 colon);
742 virtdir_add(&iscsi, name, cc, 'l', tv.v[tv.c].product,
743 strlen(tv.v[tv.c].product));
744 cc = snprintf(name, sizeof(name), "/%s/%s/version", host,
745 colon);
746 virtdir_add(&iscsi, name, cc, 'l', tv.v[tv.c].version,
747 strlen(tv.v[tv.c].version));
748 if (tv.v[tv.c].serial[0] && tv.v[tv.c].serial[0] != ' ') {
749 cc = snprintf(name, sizeof(name), "/%s/%s/serial",
750 host, colon);
751 virtdir_add(&iscsi, name, cc, 'l', tv.v[tv.c].serial,
752 strlen(tv.v[tv.c].serial));
754 tv.c += 1;
756 return fuse_main(argc - optind, argv + optind, &iscsiops, NULL);