1 /*-
2  * Copyright (c) 2007,2009 The NetBSD Foundation, Inc.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The NetBSD Foundation
6  * by Alistair Crooks (agc@netbsd.org)
7  *
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.
16  *
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.
28  */
29 #include <sys/types.h>
30 
31 #define FUSE_USE_VERSION      26
32 
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>
41 
42 #include "scsi_cmd_codes.h"
43 #include "iscsi.h"
44 #include "initiator.h"
45 #include "tests.h"
46 
47 #include "virtdir.h"
48 
49 #if defined(__NetBSD__) && defined(USE_LIBKMOD)
50 #include "libkmod.h"
51 #endif
52 
53 #include "defs.h"
54 
55 static int                     verbose; /* how chatty are we? */
56 
57 static virtdir_t     iscsi;
58 
59 enum {
60           VendorLen = 8,
61           ProductLen = 16,
62           VersionLen = 4,
63 
64           SGsize = 131072
65 };
66 
67 
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;
87 
88 DEFINE_ARRAY(targetv_t, targetinfo_t);
89 
90 static targetv_t    tv;       /* target vector of targetinfo_t structs */
91 
92 /* iqns and target addresses are returned as pairs in this dynamic array */
93 static strv_t                 all_targets;
94 
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;
100 
101 #ifndef __UNCONST
102 #define __UNCONST(x)          (x)
103 #endif
104 
105 static void
lba2cdb(uint8_t * cdb,uint32_t * lba,uint16_t * len)106 lba2cdb(uint8_t *cdb, uint32_t *lba, uint16_t *len)
107 {
108           /* Some platforms (like strongarm) aligns on */
109           /* word boundaries.  So HTONL and NTOHL won't */
110           /* work here. */
111           int       little_endian = 1;
112 
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];
129           }
130 }
131 
132 /* read the capacity (maximum LBA and blocksize) from the target */
133 int
read_capacity(uint64_t target,uint32_t lun,uint32_t * maxlba,uint32_t * blocklen)134 read_capacity(uint64_t target, uint32_t lun, uint32_t *maxlba, uint32_t *blocklen)
135 {
136           iscsi_scsi_cmd_args_t         args;
137           initiator_cmd_t               cmd;
138           uint8_t                       data[8];
139           uint8_t                       cdb[16];
140 
141           (void) memset(cdb, 0x0, sizeof(cdb));
142           cdb[0] = READ_CAPACITY;
143           cdb[1] = lun << 5;
144 
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;
151 
152           (void) memset(&cmd, 0, sizeof(initiator_cmd_t));
153 
154           cmd.isid = target;
155           cmd.type = ISCSI_SCSI_CMD;
156           cmd.ptr = &args;
157 
158           if (initiator_command(&cmd) != 0) {
159                     iscsi_err(__FILE__, __LINE__, "initiator_command() failed\n");
160                     return -1;
161           }
162           if (args.status) {
163                     iscsi_err(__FILE__, __LINE__, "READ_CAPACITY failed (status %#x)\n", args.status);
164                     return -1;
165           }
166           memcpy(maxlba, data, sizeof(*maxlba));
167           *maxlba = ISCSI_NTOHL(*maxlba);
168           if (*maxlba == 0) {
169                     iscsi_err(__FILE__, __LINE__, "Device returned Maximum LBA of zero\n");
170                     return -1;
171           }
172           memcpy(blocklen, data + 4, sizeof(*blocklen));
173           *blocklen = ISCSI_NTOHL(*blocklen);
174           if (*blocklen % 2) {
175                     iscsi_err(__FILE__, __LINE__, "Device returned strange block len: %u\n", *blocklen);
176                     return -1;
177           }
178           return 0;
179 }
180 
181 /* send inquiry command to the target, to get it to identify itself */
182 static int
inquiry(uint64_t target,uint32_t lun,uint8_t type,uint8_t inquire,uint8_t * data)183 inquiry(uint64_t target, uint32_t lun, uint8_t type, uint8_t inquire, uint8_t *data)
184 {
185           iscsi_scsi_cmd_args_t         args;
186           initiator_cmd_t               cmd;
187           uint8_t                       cdb[16];
188 
189           (void) memset(cdb, 0x0, sizeof(cdb));
190           cdb[0] = INQUIRY;
191           cdb[1] = type | (lun << 5);
192           cdb[2] = inquire;
193           cdb[4] = 256 - 1;
194 
195           (void) memset(&args, 0x0, sizeof(args));
196           args.input = 1;
197           args.trans_len = 256;
198           args.cdb = cdb;
199           args.lun = lun;
200           args.recv_data = data;
201           (void) memset(&cmd, 0x0, sizeof(cmd));
202           cmd.isid = target;
203           cmd.type = ISCSI_SCSI_CMD;
204           cmd.ptr = &args;
205 
206           if (initiator_command(&cmd) != 0) {
207                     iscsi_err(__FILE__, __LINE__, "initiator_command() failed\n");
208                     return -1;
209           }
210           if (args.status) {
211                     iscsi_err(__FILE__, __LINE__, "INQUIRY failed (status %#x)\n", args.status);
212                     return -1;
213           }
214 
215           return 0;
216 }
217 
218 /* read or write a single block of information */
219 static int
blockop(uint64_t target,uint32_t lun,uint32_t lba,uint32_t len,uint32_t blocklen,uint8_t * data,int writing)220 blockop(uint64_t target, uint32_t lun, uint32_t lba, uint32_t len,
221                 uint32_t blocklen, uint8_t *data, int writing)
222 {
223           iscsi_scsi_cmd_args_t         args;
224           initiator_cmd_t               cmd;
225           uint16_t                      readlen;
226           uint8_t                       cdb[16];
227 
228           /* Build CDB */
229           (void) memset(cdb, 0, 16);
230           cdb[0] = (writing) ? WRITE_10 : READ_10;
231           cdb[1] = lun << 5;
232           readlen = (uint16_t) len;
233           lba2cdb(cdb, &lba, &readlen);
234 
235           /* Build SCSI command */
236           (void) memset(&args, 0x0, sizeof(args));
237           if (writing) {
238                     args.send_data = data;
239                     args.output = 1;
240           } else {
241                     args.recv_data = data;
242                     args.input = 1;
243           }
244           args.lun = lun;
245           args.trans_len = len*blocklen;
246           args.length = len*blocklen;
247           args.cdb = cdb;
248           (void) memset(&cmd, 0, sizeof(initiator_cmd_t));
249           cmd.isid = target;
250           cmd.type = ISCSI_SCSI_CMD;
251           cmd.ptr = &args;
252           /* Execute iSCSI command */
253           if (initiator_command(&cmd) != 0) {
254                     iscsi_err(__FILE__, __LINE__, "initiator_command() failed\n");
255                     return -1;
256           }
257 
258           if (args.status) {
259                     iscsi_err(__FILE__, __LINE__, "scsi_command() failed (status %#x)\n", args.status);
260                     return -1;
261           }
262           return 0;
263 }
264 
265 /* perform a scatter/gather block operation */
266 static int
sgblockop(uint64_t target,uint32_t lun,uint32_t lba,uint32_t len,uint32_t blocklen,uint8_t * data,int sglen,int writing)267 sgblockop(uint64_t target, uint32_t lun, uint32_t lba, uint32_t len,
268                 uint32_t blocklen, uint8_t *data, int sglen, int writing)
269 {
270           iscsi_scsi_cmd_args_t         args;
271           initiator_cmd_t               cmd;
272           uint16_t            readlen;
273           uint8_t                       cdb[16];
274 
275           /* Build CDB */
276 
277           (void) memset(cdb, 0, 16);
278           cdb[0] = (writing) ? WRITE_10 : READ_10;
279           cdb[1] = lun << 5;
280           readlen = (uint16_t) len;
281           lba2cdb(cdb, &lba, &readlen);
282 
283           /* Build iSCSI command */
284           (void) memset(&args, 0x0, sizeof(args));
285           args.lun = lun;
286           args.output = (writing) ? 1 : 0;
287           args.input = (writing) ? 0 : 1;
288           args.trans_len = len * blocklen;
289           args.length = len * blocklen;
290           args.send_data = (writing) ? data : NULL;
291           args.send_sg_len = (writing) ? sglen : 0;
292           args.recv_data = (writing) ? NULL : data;
293           args.recv_sg_len = (writing) ? 0 : sglen;
294           args.cdb = cdb;
295           memset(&cmd, 0, sizeof(initiator_cmd_t));
296           cmd.isid = target;
297           cmd.ptr = &args;
298           cmd.type = ISCSI_SCSI_CMD;
299 
300           /* Execute iSCSI command */
301 
302           if (initiator_command(&cmd) != 0) {
303                     iscsi_err(__FILE__, __LINE__, "initiator_command() failed\n");
304                     return -1;
305           }
306           if (args.status) {
307                     iscsi_err(__FILE__, __LINE__, "scsi_command() failed (status %#x)\n", args.status);
308                     return -1;
309           }
310           return 0;
311 }
312 
313 /* read info from the target - method depends on size of data being read */
314 static int
targetop(uint32_t t,uint64_t offset,uint32_t length,uint32_t request,char * buf,int writing)315 targetop(uint32_t t, uint64_t offset, uint32_t length, uint32_t request, char *buf, int writing)
316 {
317           struct iovec        *iov;
318           uint32_t   ioc;
319           uint32_t   i;
320           int                  req_len;
321 
322           if (request > SGsize) {
323                     /* split up request into blocksize chunks */
324                     ioc = request / SGsize;
325                     if ((ioc * SGsize) < request)
326                               ioc++;
327                     if ((iov = iscsi_malloc(ioc * sizeof(*iov))) == NULL) {
328                               iscsi_err(__FILE__, __LINE__, "out of memory\n");
329                               return -1;
330                     }
331 
332                     for (i = 0 ; i < ioc ; i++) {
333                               iov[i].iov_base = &buf[i * SGsize];
334                               if (i == (ioc - 1)) { /* last one */
335                                         iov[i].iov_len = request - (i * SGsize);
336                               } else {
337                                         iov[i].iov_len = SGsize;
338                               }
339                     }
340 
341                     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) {
342                               iscsi_free(iov);
343                               iscsi_err(__FILE__, __LINE__, "read_10() failed\n");
344                               return -1;
345                     }
346                     iscsi_free(iov);
347           } else {
348                     req_len = length / tv.v[t].blocksize;
349                     if ((req_len * tv.v[t].blocksize) < length)
350                               req_len++;
351                     if (blockop(tv.v[t].target, tv.v[t].lun, offset / tv.v[t].blocksize,
352                                         req_len, tv.v[t].blocksize, (uint8_t *) buf, writing) != 0) {
353                               iscsi_err(__FILE__, __LINE__, "read_10() failed\n");
354                               return -1;
355                     }
356           }
357           return 0;
358 }
359 
360 
361 /****************************************************************************/
362 
363 /* perform the stat operation */
364 /* if this is the root, then just synthesise the data */
365 /* otherwise, retrieve the data, and be sure to fill in the size */
366 static int
iscsifs_getattr(const char * path,struct stat * st)367 iscsifs_getattr(const char *path, struct stat *st)
368 {
369           virt_dirent_t       *ep;
370           sti_t               *p;
371 
372           if (strcmp(path, "/") == 0) {
373                     (void) memset(st, 0x0, sizeof(*st));
374                     st->st_mode = S_IFDIR | 0755;
375                     st->st_nlink = 2;
376                     return 0;
377           }
378           if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) {
379                     return -ENOENT;
380           }
381           switch(ep->type) {
382           case 'b':
383                     (void) memcpy(st, &iscsi.file, sizeof(*st));
384                     st->st_mode = (S_IFBLK | 0644);
385                     break;
386           case 'c':
387                     (void) memcpy(st, &iscsi.file, sizeof(*st));
388                     st->st_mode = (S_IFCHR | 0644);
389                     break;
390           case 'd':
391                     (void) memcpy(st, &iscsi.dir, sizeof(*st));
392                     break;
393           case 'f':
394                     (void) memcpy(st, &iscsi.file, sizeof(*st));
395                     p = (sti_t *) ep->tgt;
396                     st->st_size = p->st.st_size;
397                     break;
398           case 'l':
399                     (void) memcpy(st, &iscsi.lnk, sizeof(*st));
400                     st->st_size = ep->tgtlen;
401                     break;
402           default:
403                     warn("unknown directory type `%c'", ep->type);
404                     return -ENOENT;
405           }
406           st->st_ino = ep->ino;
407           return 0;
408 }
409 
410 /* readdir operation */
411 static int
iscsifs_readdir(const char * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fi)412 iscsifs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
413                 off_t offset, struct fuse_file_info * fi)
414 {
415           virt_dirent_t       *dp;
416           VIRTDIR             *dirp;
417 
418           if ((dirp = openvirtdir(&iscsi, path)) == NULL) {
419                     return 0;
420           }
421           filler(buf, ".", NULL, 0);
422           filler(buf, "..", NULL, 0);
423           while ((dp = readvirtdir(dirp)) != NULL) {
424                     filler(buf, dp->d_name, NULL, 0);
425           }
426           closevirtdir(dirp);
427           return 0;
428 }
429 
430 /* open the file in the file system */
431 static int
iscsifs_open(const char * path,struct fuse_file_info * fi)432 iscsifs_open(const char *path, struct fuse_file_info *fi)
433 {
434           virt_dirent_t       *ep;
435           const char          *slash;
436 
437           if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) {
438                     return -ENOENT;
439           }
440           /* check path is the correct one */
441           if ((slash = strrchr(path, '/')) == NULL) {
442                     slash = path;
443           } else {
444                     slash += 1;
445           }
446           if (strcmp(slash, "storage") != 0) {
447                     return -ENOENT;
448           }
449           return 0;
450 }
451 
452 /* read the storage from the iSCSI target */
453 static int
iscsifs_read(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fi)454 iscsifs_read(const char *path, char *buf, size_t size, off_t offset,
455              struct fuse_file_info * fi)
456 {
457           virt_dirent_t       *ep;
458           uint64_t target;
459           sti_t *p;
460 
461           if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) {
462                     return -ENOENT;
463           }
464 
465           p = (sti_t *)ep->tgt;
466           target = p->target;
467 
468           if (targetop(target, offset, size, size, buf, 0) < 0) {
469                     return -EPERM;
470           }
471           return size;
472 }
473 
474 /* write the file's contents to the file system */
475 static int
iscsifs_write(const char * path,const char * buf,size_t size,off_t offset,struct fuse_file_info * fi)476 iscsifs_write(const char *path, const char *buf, size_t size, off_t offset,
477              struct fuse_file_info * fi)
478 {
479           virt_dirent_t       *ep;
480         uint64_t     target;
481         sti_t                 *p;
482 
483           if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) {
484                     return -ENOENT;
485           }
486 
487           p = (sti_t *)ep->tgt;
488           target = p->target;
489 
490           if (targetop(target, offset, size, size, __UNCONST(buf), 1) < 0) {
491                     return -EPERM;
492           }
493           return size;
494 }
495 
496 /* fill in the statvfs struct */
497 static int
iscsifs_statfs(const char * path,struct statvfs * st)498 iscsifs_statfs(const char *path, struct statvfs *st)
499 {
500           (void) memset(st, 0x0, sizeof(*st));
501           return 0;
502 }
503 
504 /* read the symbolic link */
505 static int
iscsifs_readlink(const char * path,char * buf,size_t size)506 iscsifs_readlink(const char *path, char *buf, size_t size)
507 {
508           virt_dirent_t       *ep;
509 
510           if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) {
511                     return -ENOENT;
512           }
513           if (ep->tgt == NULL) {
514                     return -ENOENT;
515           }
516           (void) strlcpy(buf, ep->tgt, size);
517           return 0;
518 }
519 
520 /* operations struct */
521 static struct fuse_operations iscsiops = {
522           .getattr = iscsifs_getattr,
523           .readlink = iscsifs_readlink,
524           .readdir = iscsifs_readdir,
525           .open = iscsifs_open,
526           .read = iscsifs_read,
527           .write = iscsifs_write,
528           .statfs = iscsifs_statfs
529 };
530 
531 int
main(int argc,char ** argv)532 main(int argc, char **argv)
533 {
534           iscsi_initiator_t   ini;
535           initiator_target_t  tinfo;
536           unsigned            u;
537           uint32_t            lbac;
538           uint32_t            blocksize;
539           uint8_t                       data[256];
540           sti_t                         sti;
541           char                          hostname[1024];
542           char                          name[1024];
543           char                          *colon;
544           char                       *host;
545           char                          buf[32];
546           char                          devtype;
547           int                           discover;
548           int                           cc;
549           int                           i;
550           uint32_t            max_targets;
551 
552           (void) memset(&tinfo, 0x0, sizeof(tinfo));
553           iscsi_initiator_set_defaults(&ini);
554           (void) gethostname(host = hostname, sizeof(hostname));
555           discover = 0;
556           (void) stat("/etc/hosts", &sti.st);
557           devtype = 'f';
558           iscsi_initiator_setvar(&ini, "address family", "4");
559           max_targets = iscsi_initiator_get_max_targets();
560 
561           while ((i = getopt(argc, argv, "46a:bcd:Dfh:p:t:u:v:V")) != -1) {
562                     switch(i) {
563                     case '4':
564                     case '6':
565                               buf[0] = i;
566                               buf[1] = 0x0;
567                               iscsi_initiator_setvar(&ini, "address family", buf);
568                               break;
569                     case 'a':
570                               iscsi_initiator_setvar(&ini, "auth type", optarg);
571                               break;
572                     case 'b':
573                               devtype = 'b';
574                               break;
575                     case 'c':
576                               devtype = 'c';
577                               break;
578                     case 'd':
579                               iscsi_initiator_setvar(&ini, "digest type", optarg);
580                               break;
581                     case 'D':
582                               discover = 1;
583                               break;
584                     case 'f':
585                               devtype = 'f';
586                               break;
587                     case 'h':
588                               iscsi_initiator_setvar(&ini, "target hostname", optarg);
589                               break;
590                     case 'p':
591                               iscsi_initiator_setvar(&ini, "target port", optarg);
592                               break;
593                     case 't':
594                               iscsi_initiator_setvar(&ini, "target instance", optarg);
595                               break;
596                     case 'u':
597                               iscsi_initiator_setvar(&ini, "user", optarg);
598                               break;
599                     case 'V':
600                               (void) printf("\"%s\" %s\nPlease send all bug reports "
601                                                   "to %s\n",
602                                                   PACKAGE_NAME,
603                                                   PACKAGE_VERSION,
604                                                   PACKAGE_BUGREPORT);
605                               exit(EXIT_SUCCESS);
606                               /* NOTREACHED */
607                     case 'v':
608                               verbose += 1;
609                               if (strcmp(optarg, "net") == 0) {
610                                         iscsi_initiator_setvar(&ini, "debug", "net");
611                               } else if (strcmp(optarg, "iscsi") == 0) {
612                                         iscsi_initiator_setvar(&ini, "debug", "iscsi");
613                               } else if (strcmp(optarg, "scsi") == 0) {
614                                         iscsi_initiator_setvar(&ini, "debug", "scsi");
615                               } else if (strcmp(optarg, "all") == 0) {
616                                         iscsi_initiator_setvar(&ini, "debug", "all");
617                               }
618                               break;
619                     default:
620                               (void) fprintf(stderr, "%s: unknown option `%c'",
621                                                   *argv, i);
622                     }
623           }
624           if (!strcmp(iscsi_initiator_getvar(&ini, "auth type"), "chap") &&
625               iscsi_initiator_getvar(&ini, "user") == NULL) {
626                     iscsi_err(__FILE__, __LINE__, "user must be specified with "
627                         "-u if using CHAP authentication\n");
628                     exit(EXIT_FAILURE);
629           }
630 
631           if (strcmp(iscsi_initiator_getvar(&ini, "auth type"), "none") &&
632               iscsi_initiator_getvar(&ini, "user") != NULL) {
633                     /*
634                      * For backwards compatibility, default to using CHAP
635                      * if username given
636                      */
637                     iscsi_initiator_setvar(&ini, "auth type", "chap");
638           }
639 
640           if (iscsi_initiator_start(&ini) == -1) {
641                     iscsi_err(__FILE__, __LINE__, "initiator_init() failed\n");
642                     exit(EXIT_FAILURE);
643           }
644 
645         if (iscsi_initiator_discover(host, 0, 0) < 0) {
646                 printf("initiator_discover() in discover failed\n");
647                     exit(EXIT_FAILURE);
648           }
649 
650         if (iscsi_initiator_get_targets(0,&all_targets) == -1) {
651                     iscsi_err(__FILE__, __LINE__,
652                               "initiator_get_targets() failed\n");
653                     exit(EXIT_FAILURE);
654           }
655 
656 
657         if (discover) {
658                     printf("Targets available from host %s:\n",host);
659                     for (u = 0; u < all_targets.c ; u += 2) {
660                               printf("%s at %s\n", all_targets.v[u],
661                                         all_targets.v[u + 1]);
662                     }
663 
664                 exit(EXIT_SUCCESS);
665         }
666 
667           if (all_targets.c/2 > max_targets) {
668                     (void) fprintf(stderr,
669                               "CONFIG_INITIATOR_NUM_TARGETS in initiator.h "
670                               "is too small.  %d targets available, "
671                               "only %d configurable.\n",
672                               all_targets.c/2, max_targets);
673                     (void) fprintf(stderr,
674                               "To increase this value, libiscsi will have be "
675                               "recompiled.\n");
676                     (void) fprintf(stderr,
677                               "Truncating number of targets to %d.\n",
678                               max_targets);
679                     all_targets.c = 2 * max_targets;
680           }
681 
682           sti.st.st_ino = 0x15c51;
683 
684 #if defined(__NetBSD__) && defined(USE_LIBKMOD)
685           /* check that the puffs module is loaded on NetBSD */
686           if (kmodstat("puffs", NULL) == 0 && !kmodload("puffs")) {
687                     (void) fprintf(stderr, "initiator: can't load puffs module\n");
688           }
689 #endif
690 
691           for (u = 0 ; u < all_targets.c / 2 ; u++) {
692                     ALLOC(targetinfo_t, tv.v, tv.size, tv.c, 10, 10, "iscsifs",
693                                         exit(EXIT_FAILURE));
694 
695                     initiator_set_target_name(u, all_targets.v[u * 2]);
696 
697                     if (iscsi_initiator_discover(host, u, 0) < 0) {
698                               printf("iscsi_initiator_discover() failed\n");
699                               break;
700                     }
701 
702                     get_target_info(u, &tinfo);
703                     if ((colon = strrchr(tinfo.TargetName, ':')) == NULL) {
704                               colon = tinfo.TargetName;
705                     } else {
706                               colon += 1;
707                     }
708 
709                     /* stuff size into st.st_size */
710                     {
711                               int retry = 5;
712                               while (retry > 0) {
713                                         if (read_capacity(u, 0, &lbac, &blocksize) == 0)
714                                                   break;
715                                         retry--;
716                                         iscsi_warn(__FILE__, __LINE__,
717                                             "read_capacity failed - retrying %d\n", retry);
718                                         sleep(1);
719                               }
720                               if (retry == 0) {
721                                         iscsi_err(__FILE__, __LINE__, "read_capacity failed - giving up\n");
722                                         break;
723                               }
724                     }
725                     sti.st.st_size = (off_t)(((uint64_t)lbac + 1) * blocksize);
726                     sti.target = u;
727 
728                     tv.v[tv.c].host = strdup(tinfo.name);
729                     tv.v[tv.c].ip = strdup(tinfo.ip);
730                     tv.v[tv.c].targetname = strdup(tinfo.TargetName);
731                     tv.v[tv.c].stargetname = strdup(colon);
732                     tv.v[tv.c].target = u;
733                     tv.v[tv.c].lun = 0;
734                     tv.v[tv.c].lbac = lbac;
735                     tv.v[tv.c].blocksize = blocksize;
736 
737                     /* get iSCSI target information */
738                     (void) memset(data, 0x0, sizeof(data));
739                     inquiry(u, 0, 0, 0, data);
740                     tv.v[tv.c].devicetype = (data[0] & 0x1f);
741                     (void) memcpy(tv.v[tv.c].vendor, &data[8], VendorLen);
742                     (void) memcpy(tv.v[tv.c].product, &data[8 + VendorLen],
743                                         ProductLen);
744                     (void) memcpy(tv.v[tv.c].version,
745                                         &data[8 + VendorLen + ProductLen], VersionLen);
746                     (void) memset(data, 0x0, sizeof(data));
747                     inquiry(u, 0, INQUIRY_EVPD_BIT, INQUIRY_UNIT_SERIAL_NUMBER_VPD,
748                                         data);
749                     tv.v[tv.c].serial = strdup((char *)&data[4]);
750 
751                     /* create the tree using virtdir routines */
752                     cc = snprintf(name, sizeof(name), "/%s", colon);
753                     virtdir_add(&iscsi, name, cc, 'd', name, cc);
754                     cc = snprintf(name, sizeof(name), "/%s/storage", colon);
755                     virtdir_add(&iscsi, name, cc, devtype, (void *)&sti,
756                                         sizeof(sti));
757                     cc = snprintf(name, sizeof(name), "/%s/hostname", colon);
758                     virtdir_add(&iscsi, name, cc, 'l', tinfo.name,
759                                         strlen(tinfo.name));
760                     cc = snprintf(name, sizeof(name), "/%s/ip", colon);
761                     virtdir_add(&iscsi, name, cc, 'l', tinfo.ip, strlen(tinfo.ip));
762                     cc = snprintf(name, sizeof(name), "/%s/targetname", colon);
763                     virtdir_add(&iscsi, name, cc, 'l', tinfo.TargetName,
764                                         strlen(tinfo.TargetName));
765                     cc = snprintf(name, sizeof(name), "/%s/vendor", colon);
766                     virtdir_add(&iscsi, name, cc, 'l', tv.v[tv.c].vendor,
767                                         strlen(tv.v[tv.c].vendor));
768                     cc = snprintf(name, sizeof(name), "/%s/product", colon);
769                     virtdir_add(&iscsi, name, cc, 'l', tv.v[tv.c].product,
770                                         strlen(tv.v[tv.c].product));
771                     cc = snprintf(name, sizeof(name), "/%s/version", colon);
772                     virtdir_add(&iscsi, name, cc, 'l', tv.v[tv.c].version,
773                                         strlen(tv.v[tv.c].version));
774                     if (tv.v[tv.c].serial[0] && tv.v[tv.c].serial[0] != ' ') {
775                               cc = snprintf(name, sizeof(name), "/%s/serial",
776                                         colon);
777                               virtdir_add(&iscsi, name, cc, 'l', tv.v[tv.c].serial,
778                                         strlen(tv.v[tv.c].serial));
779                     }
780                     tv.c += 1;
781           }
782           return fuse_main(argc - optind, argv + optind, &iscsiops, NULL);
783 }
784