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 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 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 *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; 171 } 172 if (*blocklen % 2) { 173 iscsi_err(__FILE__, __LINE__, "Device returned strange block len: %u\n", *blocklen); 174 return -1; 175 } 176 return 0; 177 } 178 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) 182 { 183 iscsi_scsi_cmd_args_t args; 184 initiator_cmd_t cmd; 185 uint8_t cdb[16]; 186 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; 192 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; 203 204 if (initiator_command(&cmd) != 0) { 205 iscsi_err(__FILE__, __LINE__, "initiator_command() failed\n"); 206 return -1; 207 } 208 if (args.status) { 209 iscsi_err(__FILE__, __LINE__, "INQUIRY failed (status %#x)\n", args.status); 210 return -1; 211 } 212 213 return 0; 214 } 215 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) 220 { 221 iscsi_scsi_cmd_args_t args; 222 initiator_cmd_t cmd; 223 uint16_t readlen; 224 uint8_t cdb[16]; 225 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); 232 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; 241 } 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; 254 } 255 256 if (args.status) { 257 iscsi_err(__FILE__, __LINE__, "scsi_command() failed (status %#x)\n", args.status); 258 return -1; 259 } 260 return 0; 261 } 262 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) 267 { 268 iscsi_scsi_cmd_args_t args; 269 initiator_cmd_t cmd; 270 uint16_t readlen; 271 uint8_t cdb[16]; 272 273 /* Build CDB */ 274 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); 280 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; 297 298 /* Execute iSCSI command */ 299 300 if (initiator_command(&cmd) != 0) { 301 iscsi_err(__FILE__, __LINE__, "initiator_command() failed\n"); 302 return -1; 303 } 304 if (args.status) { 305 iscsi_err(__FILE__, __LINE__, "scsi_command() failed (status %#x)\n", args.status); 306 return -1; 307 } 308 return 0; 309 } 310 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) 314 { 315 struct iovec *iov; 316 uint32_t ioc; 317 uint32_t i; 318 int req_len; 319 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; 328 } 329 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; 336 } 337 } 338 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; 343 } 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; 353 } 354 } 355 return 0; 356 } 357 358 359 /****************************************************************************/ 360 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) 366 { 367 virt_dirent_t *ep; 368 sti_t *p; 369 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; 375 } 376 if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) { 377 return -ENOENT; 378 } 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; 403 } 404 st->st_ino = ep->ino; 405 return 0; 406 } 407 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) 412 { 413 virt_dirent_t *dp; 414 VIRTDIR *dirp; 415 416 if ((dirp = openvirtdir(&iscsi, path)) == NULL) { 417 return 0; 418 } 419 filler(buf, ".", NULL, 0); 420 filler(buf, "..", NULL, 0); 421 while ((dp = readvirtdir(dirp)) != NULL) { 422 filler(buf, dp->d_name, NULL, 0); 423 } 424 closevirtdir(dirp); 425 return 0; 426 } 427 428 /* open the file in the file system */ 429 static int 430 iscsifs_open(const char *path, struct fuse_file_info *fi) 431 { 432 virt_dirent_t *ep; 433 const char *slash; 434 435 if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) { 436 return -ENOENT; 437 } 438 /* check path is the correct one */ 439 if ((slash = strrchr(path, '/')) == NULL) { 440 slash = path; 441 } else { 442 slash += 1; 443 } 444 if (strcmp(slash, "storage") != 0) { 445 return -ENOENT; 446 } 447 return 0; 448 } 449 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) 454 { 455 virt_dirent_t *ep; 456 uint64_t target; 457 sti_t *p; 458 459 if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) { 460 return -ENOENT; 461 } 462 463 p = (sti_t *)ep->tgt; 464 target = p->target; 465 466 if (targetop(target, offset, size, size, buf, 0) < 0) { 467 return -EPERM; 468 } 469 return size; 470 } 471 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) 476 { 477 virt_dirent_t *ep; 478 uint64_t target; 479 sti_t *p; 480 481 if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) { 482 return -ENOENT; 483 } 484 485 p = (sti_t *)ep->tgt; 486 target = p->target; 487 488 if (targetop(target, offset, size, size, __UNCONST(buf), 1) < 0) { 489 return -EPERM; 490 } 491 return size; 492 } 493 494 /* fill in the statvfs struct */ 495 static int 496 iscsifs_statfs(const char *path, struct statvfs *st) 497 { 498 (void) memset(st, 0x0, sizeof(*st)); 499 return 0; 500 } 501 502 /* read the symbolic link */ 503 static int 504 iscsifs_readlink(const char *path, char *buf, size_t size) 505 { 506 virt_dirent_t *ep; 507 508 if ((ep = virtdir_find(&iscsi, path, strlen(path))) == NULL) { 509 return -ENOENT; 510 } 511 if (ep->tgt == NULL) { 512 return -ENOENT; 513 } 514 (void) strlcpy(buf, ep->tgt, size); 515 return 0; 516 } 517 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 527 }; 528 529 int 530 main(int argc, char **argv) 531 { 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; 549 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"); 614 } 615 break; 616 default: 617 (void) fprintf(stderr, "%s: unknown option `%c'", 618 *argv, i); 619 } 620 } 621 if (iscsi_initiator_getvar(&ini, "user") == NULL) { 622 iscsi_err(__FILE__, __LINE__, "user must be specified with -u\n"); 623 exit(EXIT_FAILURE); 624 } 625 626 if (iscsi_initiator_start(&ini) == -1) { 627 iscsi_err(__FILE__, __LINE__, "initiator_init() failed\n"); 628 exit(EXIT_FAILURE); 629 } 630 631 if (iscsi_initiator_discover(host, 0, 0) < 0) { 632 printf("initiator_discover() in discover failed\n"); 633 exit(EXIT_FAILURE); 634 } 635 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); 640 } 641 642 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]); 648 } 649 650 exit(EXIT_SUCCESS); 651 } 652 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; 663 } 664 665 sti.st.st_ino = 0x15c51; 666 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"); 671 } 672 #endif 673 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)); 677 678 initiator_set_target_name(u, all_targets.v[u * 2]); 679 680 if (iscsi_initiator_discover(host, u, 0) < 0) { 681 printf("iscsi_initiator_discover() failed\n"); 682 break; 683 } 684 685 get_target_info(u, &tinfo); 686 if ((colon = strrchr(tinfo.TargetName, ':')) == NULL) { 687 colon = tinfo.TargetName; 688 } else { 689 colon += 1; 690 } 691 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; 696 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; 705 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]); 719 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)); 753 } 754 tv.c += 1; 755 } 756 return fuse_main(argc - optind, argv + optind, &iscsiops, NULL); 757 } 758