2 * Copyright (c) 2007,2009 The NetBSD Foundation, Inc.
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
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
42 #include "scsi_cmd_codes.h"
44 #include "initiator.h"
49 #if defined(__NetBSD__) && defined(USE_LIBKMOD)
55 static int verbose
; /* how chatty are we? */
57 static virtdir_t iscsi
;
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 */
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 */
102 #define __UNCONST(x) (x)
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 */
111 int little_endian
= 1;
113 if (*(char *) (void *) &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];
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 */
134 read_capacity(uint64_t target
, uint32_t lun
, uint32_t *maxlba
, uint32_t *blocklen
)
136 iscsi_scsi_cmd_args_t args
;
141 (void) memset(cdb
, 0x0, sizeof(cdb
));
142 cdb
[0] = READ_CAPACITY
;
145 (void) memset(&args
, 0x0, sizeof(args
));
146 args
.recv_data
= data
;
152 (void) memset(&cmd
, 0, sizeof(initiator_cmd_t
));
155 cmd
.type
= ISCSI_SCSI_CMD
;
158 if (initiator_command(&cmd
) != 0) {
159 iscsi_err(__FILE__
, __LINE__
, "initiator_command() failed\n");
163 iscsi_err(__FILE__
, __LINE__
, "READ_CAPACITY failed (status %#x)\n", args
.status
);
166 *maxlba
= ISCSI_NTOHL(*((uint32_t *) (data
)));
167 *blocklen
= ISCSI_NTOHL(*((uint32_t *) (data
+ 4)));
169 iscsi_err(__FILE__
, __LINE__
, "Device returned Maximum LBA of zero\n");
173 iscsi_err(__FILE__
, __LINE__
, "Device returned strange block len: %u\n", *blocklen
);
179 /* send inquiry command to the target, to get it to identify itself */
181 inquiry(uint64_t target
, uint32_t lun
, uint8_t type
, uint8_t inquire
, uint8_t *data
)
183 iscsi_scsi_cmd_args_t args
;
187 (void) memset(cdb
, 0x0, sizeof(cdb
));
189 cdb
[1] = type
| (lun
<< 5);
193 (void) memset(&args
, 0x0, sizeof(args
));
195 args
.trans_len
= 256;
198 args
.recv_data
= data
;
199 (void) memset(&cmd
, 0x0, sizeof(cmd
));
201 cmd
.type
= ISCSI_SCSI_CMD
;
204 if (initiator_command(&cmd
) != 0) {
205 iscsi_err(__FILE__
, __LINE__
, "initiator_command() failed\n");
209 iscsi_err(__FILE__
, __LINE__
, "INQUIRY failed (status %#x)\n", args
.status
);
216 /* read or write a single block of information */
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
;
227 (void) memset(cdb
, 0, 16);
228 cdb
[0] = (writing
) ? WRITE_10
: READ_10
;
230 readlen
= (uint16_t) len
;
231 lba2cdb(cdb
, &lba
, &readlen
);
233 /* Build SCSI command */
234 (void) memset(&args
, 0x0, sizeof(args
));
236 args
.send_data
= data
;
239 args
.recv_data
= data
;
243 args
.trans_len
= len
*blocklen
;
244 args
.length
= len
*blocklen
;
246 (void) memset(&cmd
, 0, sizeof(initiator_cmd_t
));
248 cmd
.type
= ISCSI_SCSI_CMD
;
250 /* Execute iSCSI command */
251 if (initiator_command(&cmd
) != 0) {
252 iscsi_err(__FILE__
, __LINE__
, "initiator_command() failed\n");
257 iscsi_err(__FILE__
, __LINE__
, "scsi_command() failed (status %#x)\n", args
.status
);
263 /* perform a scatter/gather block operation */
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
;
275 (void) memset(cdb
, 0, 16);
276 cdb
[0] = (writing
) ? WRITE_10
: READ_10
;
278 readlen
= (uint16_t) len
;
279 lba2cdb(cdb
, &lba
, &readlen
);
281 /* Build iSCSI command */
282 (void) memset(&args
, 0x0, sizeof(args
));
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
;
293 memset(&cmd
, 0, sizeof(initiator_cmd_t
));
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");
305 iscsi_err(__FILE__
, __LINE__
, "scsi_command() failed (status %#x)\n", args
.status
);
311 /* read info from the target - method depends on size of data being read */
313 targetop(uint32_t t
, uint64_t offset
, uint32_t length
, uint32_t request
, char *buf
, int writing
)
320 if (request
> SGsize
) {
321 /* split up request into blocksize chunks */
322 ioc
= request
/ SGsize
;
323 if ((ioc
* SGsize
) < request
)
325 if ((iov
= iscsi_malloc(ioc
* sizeof(*iov
))) == NULL
) {
326 iscsi_err(__FILE__
, __LINE__
, "out of memory\n");
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
);
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) {
341 iscsi_err(__FILE__
, __LINE__
, "read_10() failed\n");
346 req_len
= length
/ tv
.v
[t
].blocksize
;
347 if ((req_len
* tv
.v
[t
].blocksize
) < length
)
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");
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 */
365 iscsifs_getattr(const char *path
, struct stat
*st
)
370 if (strcmp(path
, "/") == 0) {
371 (void) memset(st
, 0x0, sizeof(*st
));
372 st
->st_mode
= S_IFDIR
| 0755;
376 if ((ep
= virtdir_find(&iscsi
, path
, strlen(path
))) == NULL
) {
381 (void) memcpy(st
, &iscsi
.file
, sizeof(*st
));
382 st
->st_mode
= (S_IFBLK
| 0644);
385 (void) memcpy(st
, &iscsi
.file
, sizeof(*st
));
386 st
->st_mode
= (S_IFCHR
| 0644);
389 (void) memcpy(st
, &iscsi
.dir
, sizeof(*st
));
392 (void) memcpy(st
, &iscsi
.file
, sizeof(*st
));
393 p
= (sti_t
*) ep
->tgt
;
394 st
->st_size
= p
->st
.st_size
;
397 (void) memcpy(st
, &iscsi
.lnk
, sizeof(*st
));
398 st
->st_size
= ep
->tgtlen
;
401 warn("unknown directory type `%c'", ep
->type
);
404 st
->st_ino
= ep
->ino
;
408 /* readdir operation */
410 iscsifs_readdir(const char *path
, void *buf
, fuse_fill_dir_t filler
,
411 off_t offset
, struct fuse_file_info
* fi
)
416 if ((dirp
= openvirtdir(&iscsi
, path
)) == NULL
) {
419 filler(buf
, ".", NULL
, 0);
420 filler(buf
, "..", NULL
, 0);
421 while ((dp
= readvirtdir(dirp
)) != NULL
) {
422 filler(buf
, dp
->d_name
, NULL
, 0);
428 /* open the file in the file system */
430 iscsifs_open(const char *path
, struct fuse_file_info
*fi
)
435 if ((ep
= virtdir_find(&iscsi
, path
, strlen(path
))) == NULL
) {
438 /* check path is the correct one */
439 if ((slash
= strrchr(path
, '/')) == NULL
) {
444 if (strcmp(slash
, "storage") != 0) {
450 /* read the storage from the iSCSI target */
452 iscsifs_read(const char *path
, char *buf
, size_t size
, off_t offset
,
453 struct fuse_file_info
* fi
)
459 if ((ep
= virtdir_find(&iscsi
, path
, strlen(path
))) == NULL
) {
463 p
= (sti_t
*)ep
->tgt
;
466 if (targetop(target
, offset
, size
, size
, buf
, 0) < 0) {
472 /* write the file's contents to the file system */
474 iscsifs_write(const char *path
, const char *buf
, size_t size
, off_t offset
,
475 struct fuse_file_info
* fi
)
481 if ((ep
= virtdir_find(&iscsi
, path
, strlen(path
))) == NULL
) {
485 p
= (sti_t
*)ep
->tgt
;
488 if (targetop(target
, offset
, size
, size
, __UNCONST(buf
), 1) < 0) {
494 /* fill in the statvfs struct */
496 iscsifs_statfs(const char *path
, struct statvfs
*st
)
498 (void) memset(st
, 0x0, sizeof(*st
));
502 /* read the symbolic link */
504 iscsifs_readlink(const char *path
, char *buf
, size_t size
)
508 if ((ep
= virtdir_find(&iscsi
, path
, strlen(path
))) == NULL
) {
511 if (ep
->tgt
== NULL
) {
514 (void) strlcpy(buf
, ep
->tgt
, size
);
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
530 main(int argc
, char **argv
)
532 iscsi_initiator_t ini
;
533 initiator_target_t tinfo
;
550 (void) memset(&tinfo
, 0x0, sizeof(tinfo
));
551 iscsi_initiator_set_defaults(&ini
);
553 (void) gethostname(host
= hostname
, sizeof(hostname
));
555 (void) stat("/etc/hosts", &sti
.st
);
557 iscsi_initiator_setvar(&ini
, "address family", "4");
558 while ((i
= getopt(argc
, argv
, "46a:bcd:Dfh:p:t:u:v:V")) != -1) {
564 iscsi_initiator_setvar(&ini
, "address family", buf
);
567 iscsi_initiator_setvar(&ini
, "auth type", optarg
);
576 iscsi_initiator_setvar(&ini
, "digest type", optarg
);
585 iscsi_initiator_setvar(&ini
, "target hostname", optarg
);
588 iscsi_initiator_setvar(&ini
, "target port", optarg
);
591 iscsi_initiator_setvar(&ini
, "target instance", optarg
);
594 iscsi_initiator_setvar(&ini
, "user", optarg
);
597 (void) printf("\"%s\" %s\nPlease send all bug reports "
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");
617 (void) fprintf(stderr
, "%s: unknown option `%c'",
621 if (iscsi_initiator_getvar(&ini
, "user") == NULL
) {
622 iscsi_err(__FILE__
, __LINE__
, "user must be specified with -u\n");
626 if (iscsi_initiator_start(&ini
) == -1) {
627 iscsi_err(__FILE__
, __LINE__
, "initiator_init() failed\n");
631 if (iscsi_initiator_discover(host
, 0, 0) < 0) {
632 printf("initiator_discover() in discover failed\n");
636 if (iscsi_initiator_get_targets(0,&all_targets
) == -1) {
637 iscsi_err(__FILE__
, __LINE__
,
638 "initiator_get_targets() failed\n");
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]);
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");
674 for (u
= 0 ; u
< all_targets
.c
/ 2 ; u
++) {
675 ALLOC(targetinfo_t
, tv
.v
, tv
.size
, tv
.c
, 10, 10, "iscsifs",
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");
685 get_target_info(u
, &tinfo
);
686 if ((colon
= strrchr(tinfo
.TargetName
, ':')) == NULL
) {
687 colon
= tinfo
.TargetName
;
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
;
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
;
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
],
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
,
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
,
725 virtdir_add(&iscsi
, name
, cc
, devtype
, (void *)&sti
,
727 cc
= snprintf(name
, sizeof(name
), "/%s/%s/hostname", host
,
729 virtdir_add(&iscsi
, name
, cc
, 'l', 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
,
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
,
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
,
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",
751 virtdir_add(&iscsi
, name
, cc
, 'l', tv
.v
[tv
.c
].serial
,
752 strlen(tv
.v
[tv
.c
].serial
));
756 return fuse_main(argc
- optind
, argv
+ optind
, &iscsiops
, NULL
);