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