1 /*
2 * Copyright (c) 1998,1999,2000
3 * Traakan, Inc., Los Altos, CA
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice unmodified, this list of conditions, and the following
11 * disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 /*
30 * Project: NDMJOB
31 * Ident: $Id: $
32 *
33 * Description: Tape/Robot and Authentication Simulation for testing purposes.
34 * Implemented as callbacks from the generic framework.
35 * Original code extracted from ndma_tape_simulator.c and
36 * ndma_robot_simulator.c
37 *
38 */
39
40
41 #include "ndmjob.h"
42
43 #ifdef NDMOS_OPTION_TAPE_SIMULATOR
44 #ifdef NDMOS_OPTION_GAP_SIMULATOR
45 struct simu_gap {
46 uint32_t magic;
47 uint32_t rectype;
48 uint32_t prev_size;
49 uint32_t size;
50 };
51
52 #define SIMU_GAP_MAGIC 0x0BEEFEE0
53 #define SIMU_GAP_RT_(a, b, c, d) ((a << 0) + (b << 8) + (c << 16) + (d << 24))
54 #define SIMU_GAP_RT_BOT SIMU_GAP_RT_('B', 'O', 'T', '_')
55 #define SIMU_GAP_RT_DATA SIMU_GAP_RT_('D', 'A', 'T', 'A')
56 #define SIMU_GAP_RT_FILE SIMU_GAP_RT_('F', 'I', 'L', 'E')
57 #define SIMU_GAP_RT_EOT SIMU_GAP_RT_('E', 'O', 'T', '_')
58
59 /* send logical EOM with a bit less than 2 32k blocks left (due to SIMU_GAPs) */
60 #define TAPE_SIM_LOGICAL_EOM 32768 * 2
61
62 /* we sneak a peek at this global variable - probably not the best way, but
63 * it works */
64 extern off_t o_tape_limit;
65
simu_back_one(struct ndm_session * sess,int over_file_mark)66 static int simu_back_one(struct ndm_session* sess, int over_file_mark)
67 {
68 struct ndm_tape_agent* ta = sess->tape_acb;
69 struct simu_gap gap;
70 off_t cur_pos;
71 off_t new_pos;
72 int rc;
73
74 cur_pos = lseek(ta->tape_fd, (off_t)0, 1);
75
76 rc = read(ta->tape_fd, &gap, sizeof gap);
77 if (rc != sizeof gap || gap.magic != SIMU_GAP_MAGIC) goto bail_out;
78
79 new_pos = cur_pos;
80 new_pos -= sizeof gap + gap.prev_size;
81
82 ta->sent_leom = 0;
83
84 /*
85 * This is the new position. We need to update simu_prev_gap.
86 */
87
88 lseek(ta->tape_fd, new_pos, 0);
89
90 rc = read(ta->tape_fd, &gap, sizeof gap);
91 if (rc != sizeof gap || gap.magic != SIMU_GAP_MAGIC) goto bail_out;
92
93 switch (gap.rectype) {
94 case SIMU_GAP_RT_BOT:
95 /* can't actually back up to this, but update stuff */
96 ta->tape_state.file_num.value = 0;
97 ta->tape_state.blockno.value = 0;
98 /* cur_pos is now just right */
99 return 0; /* can't back up */
100
101 case SIMU_GAP_RT_EOT:
102 /* this just isn't suppose to happen */
103 goto bail_out;
104
105 case SIMU_GAP_RT_DATA:
106 /* this is always OK */
107 if (ta->tape_state.blockno.value > 0) ta->tape_state.blockno.value--;
108 lseek(ta->tape_fd, new_pos, 0);
109 return SIMU_GAP_RT_DATA;
110
111 case SIMU_GAP_RT_FILE:
112 ta->tape_state.blockno.value = 0;
113 if (!over_file_mark) {
114 lseek(ta->tape_fd, cur_pos, 0);
115 return 0;
116 }
117 if (ta->tape_state.file_num.value > 0) ta->tape_state.file_num.value--;
118 lseek(ta->tape_fd, new_pos, 0);
119 return SIMU_GAP_RT_FILE;
120
121 default:
122 /* this just isn't suppose to happen */
123 goto bail_out;
124 }
125
126 bail_out:
127 lseek(ta->tape_fd, cur_pos, 0);
128 return -1;
129 }
130
simu_forw_one(struct ndm_session * sess,int over_file_mark)131 static int simu_forw_one(struct ndm_session* sess, int over_file_mark)
132 {
133 struct ndm_tape_agent* ta = sess->tape_acb;
134 struct simu_gap gap;
135 off_t cur_pos;
136 off_t new_pos;
137 int rc;
138
139 cur_pos = lseek(ta->tape_fd, (off_t)0, 1);
140
141 rc = read(ta->tape_fd, &gap, sizeof gap);
142 if (rc != sizeof gap || gap.magic != SIMU_GAP_MAGIC) goto bail_out;
143
144 ta->sent_leom = 0;
145
146 new_pos = cur_pos;
147 new_pos += gap.size + sizeof gap;
148
149 switch (gap.rectype) {
150 case SIMU_GAP_RT_BOT:
151 /* this just isn't suppose to happen */
152 goto bail_out;
153
154 case SIMU_GAP_RT_EOT:
155 lseek(ta->tape_fd, cur_pos, 0);
156 return 0; /* can't go forward */
157
158 case SIMU_GAP_RT_DATA:
159 /* this is always OK */
160 ta->tape_state.blockno.value++;
161 lseek(ta->tape_fd, new_pos, 0);
162 return SIMU_GAP_RT_DATA;
163
164 case SIMU_GAP_RT_FILE:
165 if (!over_file_mark) {
166 lseek(ta->tape_fd, cur_pos, 0);
167 return 0;
168 }
169 ta->tape_state.blockno.value = 0;
170 ta->tape_state.file_num.value++;
171 /* cur_pos is just right */
172 return SIMU_GAP_RT_FILE;
173
174 default:
175 /* this just isn't suppose to happen */
176 goto bail_out;
177 }
178
179 bail_out:
180 lseek(ta->tape_fd, cur_pos, 0);
181 return -1;
182 }
183
simu_flush_weof(struct ndm_session * sess)184 static int simu_flush_weof(struct ndm_session* sess)
185 {
186 struct ndm_tape_agent* ta = sess->tape_acb;
187
188 if (ta->weof_on_close) {
189 /* best effort */
190 ndmos_tape_wfm(sess);
191 }
192 return 0;
193 }
194
touch_tape_lockfile(char * drive_name)195 static int touch_tape_lockfile(char* drive_name)
196 {
197 char lockfile_name[NDMOS_CONST_PATH_MAX];
198 int fd;
199
200 snprintf(lockfile_name, sizeof(lockfile_name), "%s.lck", drive_name);
201 if ((fd = open(lockfile_name, O_CREAT | O_EXCL, 0666)) < 0) { return -1; }
202
203 close(fd);
204 return 0;
205 }
206
unlink_tape_lockfile(char * drive_name)207 static void unlink_tape_lockfile(char* drive_name)
208 {
209 char lockfile_name[NDMOS_CONST_PATH_MAX];
210
211 snprintf(lockfile_name, sizeof(lockfile_name), "%s.lck", drive_name);
212 unlink(lockfile_name);
213 }
214
ndmjob_tape_open(struct ndm_session * sess,char * drive_name,int will_write)215 static ndmp9_error ndmjob_tape_open(struct ndm_session* sess,
216 char* drive_name,
217 int will_write)
218 {
219 struct ndm_tape_agent* ta = sess->tape_acb;
220 struct simu_gap gap;
221 struct stat st;
222 int read_only, omode;
223 int rc, fd;
224 char pos_symlink_name[NDMOS_CONST_PATH_MAX];
225 char pos_buf[32];
226 off_t pos = -1;
227
228 if (stat(drive_name, &st) < 0) { return NDMP9_NO_DEVICE_ERR; }
229
230 read_only = (st.st_mode & 0222) == 0;
231
232 if (!will_write) {
233 omode = 0;
234 } else {
235 if (read_only) return NDMP9_WRITE_PROTECT_ERR;
236 omode = 2; /* ndmp_write means read/write */
237 }
238
239 if (touch_tape_lockfile(drive_name) < 0) return NDMP9_DEVICE_BUSY_ERR;
240
241 fd = open(drive_name, omode);
242 if (fd < 0) { return NDMP9_PERMISSION_ERR; }
243
244 snprintf(pos_symlink_name, sizeof(pos_symlink_name), "%s.pos", drive_name);
245 if (st.st_size == 0) {
246 remove(pos_symlink_name);
247 if (will_write) {
248 gap.magic = SIMU_GAP_MAGIC;
249 gap.rectype = SIMU_GAP_RT_BOT;
250 gap.size = 0;
251 gap.prev_size = 0;
252 if (write(fd, &gap, sizeof gap) < (int)sizeof gap) {
253 close(fd);
254 return NDMP9_IO_ERR;
255 }
256
257 gap.rectype = SIMU_GAP_RT_EOT;
258 if (write(fd, &gap, sizeof gap) < (int)sizeof gap) {
259 close(fd);
260 return NDMP9_IO_ERR;
261 }
262 lseek(fd, (off_t)0, 0);
263 } else {
264 goto skip_header_check;
265 }
266 }
267
268 rc = read(fd, &gap, sizeof gap);
269 if (rc != sizeof gap) {
270 close(fd);
271 return NDMP9_NO_TAPE_LOADED_ERR;
272 }
273
274 #if 1
275 if (gap.magic != SIMU_GAP_MAGIC) {
276 close(fd);
277 return NDMP9_IO_ERR;
278 }
279 #else
280 if (gap.magic != SIMU_GAP_MAGIC || gap.rectype != SIMU_GAP_RT_BOT ||
281 gap.size != 0) {
282 close(fd);
283 return NDMP9_IO_ERR;
284 }
285 #endif
286
287 rc = readlink(pos_symlink_name, pos_buf, sizeof pos_buf);
288 if (rc > 0) {
289 pos_buf[rc] = 0;
290 pos = strtol(pos_buf, 0, 0);
291 lseek(fd, pos, 0);
292 rc = read(fd, &gap, sizeof gap);
293 if (rc == sizeof gap && gap.magic == SIMU_GAP_MAGIC) {
294 } else {
295 pos = sizeof gap;
296 }
297 lseek(fd, pos, 0);
298 }
299
300 skip_header_check:
301
302 ta->tape_fd = fd;
303 if (ta->drive_name) { NDMOS_API_FREE(ta->drive_name); }
304 ta->drive_name = NDMOS_API_STRDUP(drive_name);
305 bzero(&ta->tape_state, sizeof ta->tape_state);
306 ta->tape_state.error = NDMP9_NO_ERR;
307 ta->tape_state.state = NDMP9_TAPE_STATE_OPEN;
308 ta->tape_state.open_mode =
309 will_write ? NDMP9_TAPE_RDWR_MODE : NDMP9_TAPE_READ_MODE;
310 ta->tape_state.file_num.valid = NDMP9_VALIDITY_VALID;
311 ta->tape_state.soft_errors.valid = NDMP9_VALIDITY_VALID;
312 ta->tape_state.block_size.valid = NDMP9_VALIDITY_VALID;
313 ta->tape_state.blockno.valid = NDMP9_VALIDITY_VALID;
314 ta->tape_state.total_space.valid = NDMP9_VALIDITY_INVALID;
315 ta->tape_state.space_remain.valid = NDMP9_VALIDITY_INVALID;
316
317 ta->sent_leom = 0;
318 if (o_tape_limit) {
319 ta->tape_state.total_space.valid = NDMP9_VALIDITY_VALID;
320 ta->tape_state.total_space.value = o_tape_limit;
321 ta->tape_state.space_remain.valid = NDMP9_VALIDITY_VALID;
322 ta->tape_state.space_remain.value = o_tape_limit - st.st_size;
323 }
324
325 return NDMP9_NO_ERR;
326 }
327
ndmjob_tape_close(struct ndm_session * sess)328 static ndmp9_error ndmjob_tape_close(struct ndm_session* sess)
329 {
330 struct ndm_tape_agent* ta = sess->tape_acb;
331 off_t cur_pos;
332
333 /* TODO this is not called on an EOF from the DMA, so the lockfile
334 * will remain, although the spec says the tape service should be
335 * automatically closed */
336
337 if (ta->tape_fd < 0) { return NDMP9_DEV_NOT_OPEN_ERR; }
338
339 simu_flush_weof(sess);
340
341 cur_pos = lseek(ta->tape_fd, (off_t)0, 1);
342 if (cur_pos != -1) {
343 char pos_symlink_name[NDMOS_CONST_PATH_MAX];
344 char pos_buf[32];
345
346 snprintf(pos_symlink_name, sizeof(pos_symlink_name), "%s.pos",
347 ta->drive_name);
348 snprintf(pos_buf, sizeof(pos_buf), "%ld", (long)cur_pos);
349 if (symlink(pos_buf, pos_symlink_name) < 0) {
350 ; /* ignore error during close */
351 }
352 }
353
354 close(ta->tape_fd);
355 ta->tape_fd = -1;
356
357 unlink_tape_lockfile(ta->drive_name);
358
359 ndmos_tape_initialize(sess);
360
361 return NDMP9_NO_ERR;
362 }
363
ndmjob_tape_mtio(struct ndm_session * sess,ndmp9_tape_mtio_op op,uint32_t count,uint32_t * resid)364 static ndmp9_error ndmjob_tape_mtio(struct ndm_session* sess,
365 ndmp9_tape_mtio_op op,
366 uint32_t count,
367 uint32_t* resid)
368 {
369 struct ndm_tape_agent* ta = sess->tape_acb;
370 int rc;
371
372 *resid = count;
373
374 if (ta->tape_fd < 0) { return NDMP9_DEV_NOT_OPEN_ERR; }
375
376 /* audit for valid op and for tape mode */
377 switch (op) {
378 case NDMP9_MTIO_FSF:
379 while (*resid > 0) {
380 simu_flush_weof(sess);
381 rc = simu_forw_one(sess, 1);
382 if (rc < 0) return NDMP9_IO_ERR;
383 if (rc == 0) break;
384 if (rc == SIMU_GAP_RT_FILE) *resid -= 1;
385 }
386 break;
387
388 case NDMP9_MTIO_BSF:
389 while (*resid > 0) {
390 simu_flush_weof(sess);
391 rc = simu_back_one(sess, 1);
392 if (rc < 0) return NDMP9_IO_ERR;
393 if (rc == 0) break;
394 if (rc == SIMU_GAP_RT_FILE) *resid -= 1;
395 }
396 break;
397
398 case NDMP9_MTIO_FSR:
399 while (*resid > 0) {
400 simu_flush_weof(sess);
401 rc = simu_forw_one(sess, 0);
402 if (rc < 0) return NDMP9_IO_ERR;
403 if (rc == 0) break;
404 *resid -= 1;
405 }
406 break;
407
408 case NDMP9_MTIO_BSR:
409 while (*resid > 0) {
410 simu_flush_weof(sess);
411 rc = simu_back_one(sess, 0);
412 if (rc < 0) return NDMP9_IO_ERR;
413 if (rc == 0) break;
414 *resid -= 1;
415 }
416 break;
417
418 case NDMP9_MTIO_REW:
419 simu_flush_weof(sess);
420 *resid = 0;
421 ta->tape_state.file_num.value = 0;
422 ta->tape_state.blockno.value = 0;
423 lseek(ta->tape_fd, (off_t)(sizeof(struct simu_gap)), 0);
424 break;
425
426 case NDMP9_MTIO_OFF:
427 simu_flush_weof(sess);
428 /* Hmmm. */
429 break;
430
431 case NDMP9_MTIO_EOF: /* should be "WFM" write-file-mark */
432 if (!NDMTA_TAPE_IS_WRITABLE(ta)) { return NDMP9_PERMISSION_ERR; }
433 while (*resid > 0) {
434 ndmp9_error err;
435
436 err = ndmos_tape_wfm(sess);
437 if (err != NDMP9_NO_ERR) return err;
438
439 *resid -= 1;
440 }
441 break;
442
443 default:
444 return NDMP9_ILLEGAL_ARGS_ERR;
445 }
446
447 return NDMP9_NO_ERR;
448 }
449
ndmjob_tape_write(struct ndm_session * sess,char * buf,uint32_t count,uint32_t * done_count)450 static ndmp9_error ndmjob_tape_write(struct ndm_session* sess,
451 char* buf,
452 uint32_t count,
453 uint32_t* done_count)
454 {
455 struct ndm_tape_agent* ta = sess->tape_acb;
456 int rc;
457 struct simu_gap gap;
458 off_t cur_pos;
459 ndmp9_error err;
460 uint32_t prev_size;
461
462 if (ta->tape_fd < 0) { return NDMP9_DEV_NOT_OPEN_ERR; }
463
464 if (!NDMTA_TAPE_IS_WRITABLE(ta)) { return NDMP9_PERMISSION_ERR; }
465
466 cur_pos = lseek(ta->tape_fd, (off_t)0, 1);
467
468 if (o_tape_limit) {
469 /* if cur_pos is past LEOM, but we haven't sent NDMP9_EOM_ERR yet,
470 * then do so now */
471 if (!ta->sent_leom && cur_pos > o_tape_limit - TAPE_SIM_LOGICAL_EOM) {
472 ta->sent_leom = 1;
473 return NDMP9_EOM_ERR;
474 }
475
476 /* if this write will put us over PEOM, then send NDMP9_IO_ERR */
477 if ((off_t)(cur_pos + sizeof gap + count) > o_tape_limit) {
478 return NDMP9_IO_ERR;
479 }
480 }
481
482 rc = read(ta->tape_fd, &gap, sizeof gap);
483 if (rc != sizeof gap || gap.magic != SIMU_GAP_MAGIC) {
484 lseek(ta->tape_fd, cur_pos, 0);
485 return NDMP9_IO_ERR;
486 }
487
488 prev_size = gap.prev_size;
489
490 gap.magic = SIMU_GAP_MAGIC;
491 gap.rectype = SIMU_GAP_RT_DATA;
492 gap.prev_size = prev_size;
493 gap.size = count;
494
495 lseek(ta->tape_fd, cur_pos, 0);
496
497 if (write(ta->tape_fd, &gap, sizeof gap) == sizeof gap &&
498 (uint32_t)write(ta->tape_fd, buf, count) == count) {
499 cur_pos += count + sizeof gap;
500
501 prev_size = count;
502
503 ta->tape_state.blockno.value++;
504
505 *done_count = count;
506
507 err = NDMP9_NO_ERR;
508 } else {
509 err = NDMP9_IO_ERR;
510 }
511
512
513 if (ftruncate(ta->tape_fd, cur_pos) < 0) return NDMP9_IO_ERR;
514
515 lseek(ta->tape_fd, cur_pos, 0);
516
517 gap.rectype = SIMU_GAP_RT_EOT;
518 gap.size = 0;
519 gap.prev_size = prev_size;
520
521 if (write(ta->tape_fd, &gap, sizeof gap) < (int)sizeof gap)
522 return NDMP9_IO_ERR;
523 lseek(ta->tape_fd, cur_pos, 0);
524
525 if (o_tape_limit) {
526 ta->tape_state.space_remain.value = o_tape_limit - cur_pos;
527 }
528
529 ta->weof_on_close = 1;
530
531 return err;
532 }
533
ndmjob_tape_wfm(struct ndm_session * sess)534 static ndmp9_error ndmjob_tape_wfm(struct ndm_session* sess)
535 {
536 struct ndm_tape_agent* ta = sess->tape_acb;
537 int rc;
538 struct simu_gap gap;
539 off_t cur_pos;
540 ndmp9_error err;
541 uint32_t prev_size;
542
543 ta->weof_on_close = 0;
544
545 if (ta->tape_fd < 0) { return NDMP9_DEV_NOT_OPEN_ERR; }
546
547 if (!NDMTA_TAPE_IS_WRITABLE(ta)) { return NDMP9_PERMISSION_ERR; }
548
549 cur_pos = lseek(ta->tape_fd, (off_t)0, 1);
550
551 if (o_tape_limit) {
552 /* note: filemarks *never* trigger NDMP9_EOM_ERR */
553
554 /* if this write will put us over PEOM, then send NDMP9_IO_ERR */
555 if ((off_t)(cur_pos + sizeof gap) > o_tape_limit) { return NDMP9_IO_ERR; }
556 }
557
558 rc = read(ta->tape_fd, &gap, sizeof gap);
559 if (rc != sizeof gap || gap.magic != SIMU_GAP_MAGIC) {
560 lseek(ta->tape_fd, cur_pos, 0);
561 return NDMP9_IO_ERR;
562 }
563
564 prev_size = gap.prev_size;
565
566 gap.magic = SIMU_GAP_MAGIC;
567 gap.rectype = SIMU_GAP_RT_FILE;
568 gap.prev_size = prev_size;
569 gap.size = 0;
570
571 lseek(ta->tape_fd, cur_pos, 0);
572
573 if (write(ta->tape_fd, &gap, sizeof gap) == sizeof gap) {
574 cur_pos += sizeof gap;
575
576 prev_size = 0;
577
578 ta->tape_state.file_num.value++;
579 ta->tape_state.blockno.value = 0;
580
581 err = NDMP9_NO_ERR;
582 } else {
583 err = NDMP9_IO_ERR;
584 }
585
586 if (ftruncate(ta->tape_fd, cur_pos) < 0) return NDMP9_IO_ERR;
587 lseek(ta->tape_fd, cur_pos, 0);
588
589 gap.rectype = SIMU_GAP_RT_EOT;
590 gap.size = 0;
591 gap.prev_size = prev_size;
592
593 if (write(ta->tape_fd, &gap, sizeof gap) < (int)sizeof gap)
594 return NDMP9_IO_ERR;
595 lseek(ta->tape_fd, cur_pos, 0);
596
597 if (o_tape_limit) {
598 ta->tape_state.space_remain.value = o_tape_limit - cur_pos;
599 }
600
601 return err;
602 }
603
ndmjob_tape_read(struct ndm_session * sess,char * buf,uint32_t count,uint32_t * done_count)604 static ndmp9_error ndmjob_tape_read(struct ndm_session* sess,
605 char* buf,
606 uint32_t count,
607 uint32_t* done_count)
608 {
609 struct ndm_tape_agent* ta = sess->tape_acb;
610 int rc;
611 struct simu_gap gap;
612 off_t cur_pos;
613
614 if (ta->tape_fd < 0) { return NDMP9_DEV_NOT_OPEN_ERR; }
615
616 cur_pos = lseek(ta->tape_fd, (off_t)0, 1);
617
618 rc = read(ta->tape_fd, &gap, sizeof gap);
619 if (rc != sizeof gap || gap.magic != SIMU_GAP_MAGIC) {
620 lseek(ta->tape_fd, cur_pos, 0);
621 return NDMP9_IO_ERR;
622 }
623
624 if (gap.rectype == SIMU_GAP_RT_DATA) {
625 unsigned nb;
626
627 nb = count;
628 if (nb > gap.size) nb = gap.size;
629
630 rc = read(ta->tape_fd, buf, nb);
631 if (rc != (int)nb) {
632 lseek(ta->tape_fd, cur_pos, 0);
633 return NDMP9_IO_ERR;
634 }
635
636 if (gap.size != nb) {
637 cur_pos += sizeof gap + gap.size;
638 lseek(ta->tape_fd, cur_pos, 0);
639 }
640
641 ta->tape_state.blockno.value++;
642
643 *done_count = nb;
644 } else {
645 /* all other record types are interpretted as EOF */
646 lseek(ta->tape_fd, cur_pos, 0);
647 *done_count = 0;
648 return NDMP9_EOF_ERR;
649 }
650 return NDMP9_NO_ERR;
651 }
652 #else
simu_flush_weof(struct ndm_session * sess)653 static int simu_flush_weof(struct ndm_session* sess)
654 {
655 struct ndm_tape_agent* ta = sess->tape_acb;
656
657 if (ta->weof_on_close) {
658 /* best effort */
659 ndmos_tape_wfm(sess);
660 }
661 return 0;
662 }
663
ndmjob_tape_open(struct ndm_session * sess,char * drive_name,int will_write)664 static ndmp9_error ndmjob_tape_open(struct ndm_session* sess,
665 char* drive_name,
666 int will_write)
667 {
668 struct ndm_tape_agent* ta = sess->tape_acb;
669 struct stat st;
670 int read_only, omode;
671 int fd;
672
673 if (stat(drive_name, &st) < 0) { return NDMP9_NO_DEVICE_ERR; }
674
675 read_only = (st.st_mode & 0222) == 0;
676
677 if (!will_write) {
678 omode = 0;
679 } else {
680 if (read_only) return NDMP9_WRITE_PROTECT_ERR;
681 omode = 2; /* ndmp_write means read/write */
682 }
683
684 fd = open(drive_name, omode);
685 if (fd < 0) { return NDMP9_PERMISSION_ERR; }
686
687 if (st.st_size == 0) {
688 if (will_write) {
689 lseek(fd, (off_t)0, 0);
690 } else {
691 goto skip_header_check;
692 }
693 }
694
695 skip_header_check:
696 ta->tape_fd = fd;
697 if (ta->drive_name) { NDMOS_API_FREE(ta->drive_name); }
698 ta->drive_name = NDMOS_API_STRDUP(drive_name);
699 bzero(&ta->tape_state, sizeof ta->tape_state);
700 ta->tape_state.error = NDMP9_NO_ERR;
701 ta->tape_state.state = NDMP9_TAPE_STATE_OPEN;
702 ta->tape_state.open_mode =
703 will_write ? NDMP9_TAPE_RDWR_MODE : NDMP9_TAPE_READ_MODE;
704 ta->tape_state.file_num.valid = NDMP9_VALIDITY_VALID;
705 ta->tape_state.soft_errors.valid = NDMP9_VALIDITY_VALID;
706 ta->tape_state.block_size.valid = NDMP9_VALIDITY_VALID;
707 ta->tape_state.blockno.valid = NDMP9_VALIDITY_VALID;
708 ta->tape_state.total_space.valid = NDMP9_VALIDITY_INVALID;
709 ta->tape_state.space_remain.valid = NDMP9_VALIDITY_INVALID;
710
711 return NDMP9_NO_ERR;
712 }
713
ndmjob_tape_close(struct ndm_session * sess)714 static ndmp9_error ndmjob_tape_close(struct ndm_session* sess)
715 {
716 struct ndm_tape_agent* ta = sess->tape_acb;
717
718 if (ta->tape_fd < 0) { return NDMP9_DEV_NOT_OPEN_ERR; }
719
720 simu_flush_weof(sess);
721
722 close(ta->tape_fd);
723 ta->tape_fd = -1;
724
725 ndmos_tape_initialize(sess);
726
727 return NDMP9_NO_ERR;
728 }
729
ndmjob_tape_mtio(struct ndm_session * sess,ndmp9_tape_mtio_op op,uint32_t count,uint32_t * resid)730 static ndmp9_error ndmjob_tape_mtio(struct ndm_session* sess,
731 ndmp9_tape_mtio_op op,
732 uint32_t count,
733 uint32_t* resid)
734 {
735 struct ndm_tape_agent* ta = sess->tape_acb;
736 int rc;
737
738 *resid = 0;
739
740 if (ta->tape_fd < 0) { return NDMP9_DEV_NOT_OPEN_ERR; }
741
742
743 /* audit for valid op and for tape mode */
744 switch (op) {
745 case NDMP9_MTIO_FSF:
746 return NDMP9_NO_ERR;
747
748 case NDMP9_MTIO_BSF:
749 return NDMP9_NO_ERR;
750
751 case NDMP9_MTIO_FSR:
752 return NDMP9_NO_ERR;
753
754 case NDMP9_MTIO_BSR:
755 return NDMP9_NO_ERR;
756
757 case NDMP9_MTIO_REW:
758 simu_flush_weof(sess);
759 *resid = 0;
760 ta->tape_state.file_num.value = 0;
761 ta->tape_state.blockno.value = 0;
762 lseek(ta->tape_fd, (off_t)0, 0);
763 ndmalogf(sess, 0, 7, "NDMP9_MTIO_REW");
764 break;
765
766 case NDMP9_MTIO_OFF:
767 return NDMP9_NO_ERR;
768
769 case NDMP9_MTIO_EOF: /* should be "WFM" write-file-mark */
770 return NDMP9_NO_ERR;
771
772 default:
773 return NDMP9_ILLEGAL_ARGS_ERR;
774 }
775
776 return NDMP9_NO_ERR;
777 }
778
ndmjob_tape_write(struct ndm_session * sess,char * buf,uint32_t count,uint32_t * done_count)779 static ndmp9_error ndmjob_tape_write(struct ndm_session* sess,
780 char* buf,
781 uint32_t count,
782 uint32_t* done_count)
783 {
784 struct ndm_tape_agent* ta = sess->tape_acb;
785 off_t cur_pos;
786 ndmp9_error err;
787 uint32_t prev_size;
788 int rc;
789
790 if (ta->tape_fd < 0) { return NDMP9_DEV_NOT_OPEN_ERR; }
791
792 if (!NDMTA_TAPE_IS_WRITABLE(ta)) { return NDMP9_PERMISSION_ERR; }
793
794 cur_pos = lseek(ta->tape_fd, (off_t)0, 1);
795 lseek(ta->tape_fd, cur_pos, 0);
796
797 if ((uint32_t)write(ta->tape_fd, buf, count) == count) {
798 cur_pos += count;
799
800 prev_size = count;
801
802 ta->tape_state.blockno.value++;
803
804 *done_count = count;
805
806 err = NDMP9_NO_ERR;
807 } else {
808 ndmalogf(sess, 0, 7, "write not %d", count);
809 err = NDMP9_IO_ERR;
810 }
811
812 /* error ignored for pipe file descriptor */
813 rc = ftruncate(ta->tape_fd, cur_pos);
814
815 lseek(ta->tape_fd, cur_pos, 0);
816
817 ta->weof_on_close = 1;
818
819 return err;
820 }
821
ndmjob_tape_wfm(struct ndm_session * sess)822 static ndmp9_error ndmjob_tape_wfm(struct ndm_session* sess)
823 {
824 struct ndm_tape_agent* ta = sess->tape_acb;
825 off_t cur_pos;
826 ndmp9_error err;
827 int rc;
828
829 ta->weof_on_close = 0;
830
831 if (ta->tape_fd < 0) { return NDMP9_DEV_NOT_OPEN_ERR; }
832
833 if (!NDMTA_TAPE_IS_WRITABLE(ta)) { return NDMP9_PERMISSION_ERR; }
834
835 cur_pos = lseek(ta->tape_fd, (off_t)0, 1);
836
837 lseek(ta->tape_fd, cur_pos, 0);
838 err = NDMP9_NO_ERR;
839
840 /* error ignored for pipe file descriptor */
841 rc = ftruncate(ta->tape_fd, cur_pos);
842
843 lseek(ta->tape_fd, cur_pos, 0);
844
845 return err;
846 }
847
ndmjob_tape_read(struct ndm_session * sess,char * buf,uint32_t count,uint32_t * done_count)848 static ndmp9_error ndmjob_tape_read(struct ndm_session* sess,
849 char* buf,
850 uint32_t count,
851 uint32_t* done_count)
852 {
853 struct ndm_tape_agent* ta = sess->tape_acb;
854 size_t rc, nb;
855
856 if (ta->tape_fd < 0) { return NDMP9_DEV_NOT_OPEN_ERR; }
857
858 nb = count;
859
860 rc = read(ta->tape_fd, buf, nb);
861 if (rc < 0) { return NDMP9_IO_ERR; }
862 ta->tape_state.blockno.value++;
863
864 *done_count = rc;
865
866 if (rc == 0) { return NDMP9_EOF_ERR; }
867 return NDMP9_NO_ERR;
868 }
869 #endif /* NDMOS_OPTION_GAP_SIMULATOR */
870 #endif /* NDMOS_OPTION_TAPE_SIMULATOR */
871
872 #ifdef NDMOS_OPTION_ROBOT_SIMULATOR
873
874 #include "scsiconst.h"
875
876 /*
877 * Robot state management
878 ****************************************************************
879 */
880
881 /* xxx_FIRST must be in order! */
882 #define IE_FIRST 0
883 #define IE_COUNT 2
884 #define MTE_FIRST 16
885 #define MTE_COUNT 1
886 #define DTE_FIRST 128
887 #define DTE_COUNT 2
888 #define STORAGE_FIRST 1024
889 #define STORAGE_COUNT 10
890
891 #if (IE_FIRST + IE_COUNT > MTE_FIRST) || \
892 (MTE_FIRST + MTE_COUNT > DTE_FIRST) || \
893 (DTE_FIRST + MTE_COUNT > STORAGE_FIRST)
894 #error element addresses overlap or are in the wrong order
895 #endif
896
897 #define IS_IE_ADDR(a) ((a) >= IE_FIRST && (a) < IE_FIRST + IE_COUNT)
898 #define IS_MTE_ADDR(a) ((a) >= MTE_FIRST && (a) < MTE_FIRST + MTE_COUNT)
899 #define IS_DTE_ADDR(a) ((a) >= DTE_FIRST && (a) < DTE_FIRST + DTE_COUNT)
900 #define IS_STORAGE_ADDR(a) \
901 ((a) >= STORAGE_FIRST && (a) < STORAGE_FIRST + STORAGE_COUNT)
902
903 struct element_state {
904 int full;
905 int medium_type;
906 int source_element;
907 char pvoltag[32];
908 char avoltag[32];
909 };
910
911 struct robot_state {
912 struct element_state mte[MTE_COUNT];
913 struct element_state storage[STORAGE_COUNT];
914 struct element_state ie[IE_COUNT];
915 struct element_state dte[DTE_COUNT];
916 };
917
robot_state_init(struct robot_state * rs)918 static void robot_state_init(struct robot_state* rs)
919 {
920 int i;
921
922 /* invent some nice data, with some nice voltags and whatnot */
923
924 NDMOS_API_BZERO(rs, sizeof(*rs));
925
926 /* (nothing to do for MTEs) */
927
928 for (i = 0; i < STORAGE_COUNT; i++) {
929 struct element_state* es = &rs->storage[i];
930 es->full = 1;
931
932 es->medium_type = 1; /* data */
933 es->source_element = 0;
934 snprintf(es->pvoltag, sizeof(es->pvoltag),
935 "PTAG%02XXX ", i);
936 snprintf(es->avoltag, sizeof(es->avoltag),
937 "ATAG%02XXX ", i);
938 }
939
940 /* (i/e are all empty) */
941
942 /* (dte's are all empty) */
943 }
944
robot_state_load(struct ndm_session * sess,struct robot_state * rs)945 static void robot_state_load(struct ndm_session* sess, struct robot_state* rs)
946 {
947 int fd;
948 char filename[NDMOS_CONST_PATH_MAX];
949
950 /* N.B. writing a struct to disk like this isn't portable, but this
951 * is test code, so it's OK for now. */
952
953 snprintf(filename, sizeof filename, "%s/state", sess->robot_acb->sim_dir);
954 fd = open(filename, O_RDONLY, 0666);
955 if (fd < 0) {
956 robot_state_init(rs);
957 return;
958 }
959 if (read(fd, (void*)rs, sizeof(*rs)) < sizeof(*rs)) {
960 robot_state_init(rs);
961 close(fd);
962 return;
963 }
964
965 close(fd);
966 }
967
robot_state_save(struct ndm_session * sess,struct robot_state * rs)968 static int robot_state_save(struct ndm_session* sess, struct robot_state* rs)
969 {
970 int fd;
971 char filename[NDMOS_CONST_PATH_MAX];
972
973 /* N.B. writing a struct to disk like this isn't portable, but this
974 * is test code, so it's OK for now. */
975
976 snprintf(filename, sizeof filename, "%s/state", sess->robot_acb->sim_dir);
977 fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);
978 if (fd < 0) return -1;
979 if (write(fd, (void*)rs, sizeof(*rs)) < sizeof(*rs)) {
980 close(fd);
981 return -1;
982 }
983 close(fd);
984
985 return 0;
986 }
987
robot_state_move(struct ndm_session * sess,struct robot_state * rs,int src,int dest)988 static int robot_state_move(struct ndm_session* sess,
989 struct robot_state* rs,
990 int src,
991 int dest)
992 {
993 char src_filename[NDMOS_CONST_PATH_MAX];
994 struct element_state* src_elt;
995 char dest_filename[NDMOS_CONST_PATH_MAX];
996 struct element_state* dest_elt;
997 struct stat st;
998 char pos[NDMOS_CONST_PATH_MAX];
999
1000 /* TODO: audit that the tape device is not using this volume right now */
1001
1002 ndmalogf(sess, 0, 3, "moving medium from %d to %d", src, dest);
1003
1004 if (IS_IE_ADDR(src)) {
1005 src_elt = &rs->ie[src - IE_FIRST];
1006 snprintf(src_filename, sizeof(src_filename), "%s/ie%d",
1007 sess->robot_acb->sim_dir, src - IE_FIRST);
1008 } else if (IS_DTE_ADDR(src)) {
1009 src_elt = &rs->dte[src - DTE_FIRST];
1010 snprintf(src_filename, sizeof(src_filename), "%s/drive%d",
1011 sess->robot_acb->sim_dir, src - DTE_FIRST);
1012 } else if (IS_STORAGE_ADDR(src)) {
1013 src_elt = &rs->storage[src - STORAGE_FIRST];
1014 snprintf(src_filename, sizeof(src_filename), "%s/slot%d",
1015 sess->robot_acb->sim_dir, src - STORAGE_FIRST);
1016 } else {
1017 ndmalogf(sess, 0, 3, "invalid src address %d", src);
1018 return -1;
1019 }
1020
1021 if (IS_IE_ADDR(dest)) {
1022 dest_elt = &rs->ie[dest - IE_FIRST];
1023 snprintf(dest_filename, sizeof(dest_filename), "%s/ie%d",
1024 sess->robot_acb->sim_dir, dest - IE_FIRST);
1025 } else if (IS_DTE_ADDR(dest)) {
1026 dest_elt = &rs->dte[dest - DTE_FIRST];
1027 snprintf(dest_filename, sizeof(dest_filename), "%s/drive%d",
1028 sess->robot_acb->sim_dir, dest - DTE_FIRST);
1029 } else if (IS_STORAGE_ADDR(dest)) {
1030 dest_elt = &rs->storage[dest - STORAGE_FIRST];
1031 snprintf(dest_filename, sizeof(dest_filename), "%s/slot%d",
1032 sess->robot_acb->sim_dir, dest - STORAGE_FIRST);
1033 } else {
1034 ndmalogf(sess, 0, 3, "invalid dst address %d", src);
1035 return -1;
1036 }
1037
1038 if (!src_elt->full) {
1039 ndmalogf(sess, 0, 3, "src not full");
1040 return -1;
1041 }
1042
1043 if (dest_elt->full) {
1044 ndmalogf(sess, 0, 3, "dest full");
1045 return -1;
1046 }
1047
1048 /* OK, enough checking, let's do it */
1049 /* delete the destination, if it exists */
1050 if (stat(dest_filename, &st) >= 0) {
1051 ndmalogf(sess, 0, 3, "unlink %s", dest_filename);
1052 if (unlink(dest_filename) < 0) {
1053 ndmalogf(sess, 0, 0, "error unlinking: %s", strerror(errno));
1054 return -1;
1055 }
1056 }
1057
1058 /* and move the source if it exists */
1059 if (stat(src_filename, &st) >= 0) {
1060 ndmalogf(sess, 0, 3, "move %s to %s", src_filename, dest_filename);
1061 if (rename(src_filename, dest_filename) < 0) {
1062 ndmalogf(sess, 0, 0, "error renaming: %s", strerror(errno));
1063 return -1;
1064 }
1065 } else {
1066 /* otherwise touch the destination file */
1067 ndmalogf(sess, 0, 3, "touch %s", dest_filename);
1068 int fd = open(dest_filename, O_CREAT | O_WRONLY, 0666);
1069 if (fd < 0) {
1070 ndmalogf(sess, 0, 0, "error touching: %s", strerror(errno));
1071 return -1;
1072 }
1073 close(fd);
1074 }
1075
1076 /* blow away any tape-drive .pos files */
1077 snprintf(pos, sizeof(pos), "%s.pos", src_filename);
1078 unlink(pos); /* ignore errors */
1079 snprintf(pos, sizeof(pos), "%s.pos", dest_filename);
1080 unlink(pos); /* ignore errors */
1081
1082 /* update state */
1083 *dest_elt = *src_elt;
1084 ndmalogf(sess, 0, 3, "setting dest's source_element to %d", src);
1085 dest_elt->source_element = src;
1086 src_elt->full = 0;
1087
1088
1089 ndmalogf(sess, 0, 3, "move successful");
1090 return 0;
1091 }
1092
1093 /*
1094 * SCSI commands
1095 ****************************************************************
1096 */
1097
1098 /*
1099 * Utilities
1100 */
1101
scsi_fail_with_sense_code(struct ndm_session * sess,ndmp9_execute_cdb_reply * reply,int status,int sense_key,int asq)1102 static ndmp9_error scsi_fail_with_sense_code(struct ndm_session* sess,
1103 ndmp9_execute_cdb_reply* reply,
1104 int status,
1105 int sense_key,
1106 int asq)
1107 {
1108 unsigned char ext_sense[] = {0x72, /* current errors */
1109 sense_key & SCSI_SENSE_SENSE_KEY_MASK,
1110 (asq >> 8) & 0xff,
1111 (asq)&0xff,
1112 0,
1113 0,
1114 0,
1115 0};
1116
1117 ndmalogf(sess, 0, 3,
1118 "sending failure; status=0x%02x sense_key=0x%02x asq=0x%04x", status,
1119 sense_key, asq);
1120
1121 reply->status = status;
1122 reply->ext_sense.ext_sense_len = sizeof(ext_sense);
1123 reply->ext_sense.ext_sense_val = NDMOS_API_MALLOC(sizeof(ext_sense));
1124 if (!reply->ext_sense.ext_sense_val) return NDMP9_NO_MEM_ERR;
1125 NDMOS_API_BCOPY(ext_sense, reply->ext_sense.ext_sense_val, sizeof(ext_sense));
1126
1127 return NDMP9_NO_ERR;
1128 }
1129
1130 /*
1131 * Command implementations
1132 */
1133
execute_cdb_test_unit_ready(struct ndm_session * sess,ndmp9_execute_cdb_request * request,ndmp9_execute_cdb_reply * reply)1134 static ndmp9_error execute_cdb_test_unit_ready(
1135 struct ndm_session* sess,
1136 ndmp9_execute_cdb_request* request,
1137 ndmp9_execute_cdb_reply* reply)
1138 {
1139 if (request->cdb.cdb_len != 6)
1140 return scsi_fail_with_sense_code(sess, reply, SCSI_STATUS_CHECK_CONDITION,
1141 SCSI_SENSE_KEY_ILLEGAL_REQUEST,
1142 ASQ_INVALID_FIELD_IN_CDB);
1143
1144 /* yep, we're ready! */
1145
1146 return NDMP9_NO_ERR;
1147 }
1148
execute_cdb_inquiry(struct ndm_session * sess,ndmp9_execute_cdb_request * request,ndmp9_execute_cdb_reply * reply)1149 static ndmp9_error execute_cdb_inquiry(struct ndm_session* sess,
1150 ndmp9_execute_cdb_request* request,
1151 ndmp9_execute_cdb_reply* reply)
1152 {
1153 unsigned char* cdb = (unsigned char*)request->cdb.cdb_val;
1154 char* response;
1155 int response_len;
1156 char* p;
1157
1158 /* N.B.: only page code 0 is supported */
1159 if (request->cdb.cdb_len != 6 ||
1160 request->data_dir != NDMP9_SCSI_DATA_DIR_IN || cdb[1] & 0x01 ||
1161 cdb[2] != 0 || request->datain_len < 96 || ((cdb[3] << 8) + cdb[4]) < 96)
1162 return scsi_fail_with_sense_code(sess, reply, SCSI_STATUS_CHECK_CONDITION,
1163 SCSI_SENSE_KEY_ILLEGAL_REQUEST,
1164 ASQ_INVALID_FIELD_IN_CDB);
1165
1166 response_len = 96;
1167 p = response = NDMOS_API_MALLOC(response_len);
1168 if (!response) return NDMP9_NO_MEM_ERR;
1169 NDMOS_API_BZERO(response, response_len);
1170 *(p++) = 0x08; /* media changer */
1171 *(p++) = 0; /* RMB=0 */
1172 *(p++) = 6; /* VERSION=SPC-4 */
1173 *(p++) = 2; /* !NORMACA, !HISUP, RESPONSE DATA FORMAT = 2 */
1174 *(p++) = 92; /* remaining bytes */
1175 *(p++) = 0; /* lots of flags, all 0 */
1176 *(p++) = 0; /* lots of flags, all 0 */
1177 *(p++) = 0; /* lots of flags, all 0 */
1178 NDMOS_API_BCOPY("NDMJOB ", p, 8);
1179 p += 8;
1180 NDMOS_API_BCOPY("FakeRobot ", p, 16);
1181 p += 16;
1182 NDMOS_API_BCOPY("1.0 ", p, 4);
1183 p += 4;
1184 /* remainder is zero */
1185
1186 reply->datain.datain_len = response_len;
1187 reply->datain.datain_val = response;
1188
1189 return NDMP9_NO_ERR;
1190 }
1191
execute_cdb_mode_sense_6(struct ndm_session * sess,ndmp9_execute_cdb_request * request,ndmp9_execute_cdb_reply * reply)1192 static ndmp9_error execute_cdb_mode_sense_6(struct ndm_session* sess,
1193 ndmp9_execute_cdb_request* request,
1194 ndmp9_execute_cdb_reply* reply)
1195 {
1196 unsigned char* cdb = (unsigned char*)request->cdb.cdb_val;
1197 int page, subpage;
1198 char* response;
1199 int response_len;
1200 char* p;
1201
1202 if (request->cdb.cdb_len != 6 || request->data_dir != NDMP9_SCSI_DATA_DIR_IN)
1203 return scsi_fail_with_sense_code(sess, reply, SCSI_STATUS_CHECK_CONDITION,
1204 SCSI_SENSE_KEY_ILLEGAL_REQUEST,
1205 ASQ_INVALID_FIELD_IN_CDB);
1206 page = cdb[2] & 0x3f;
1207 subpage = cdb[3];
1208
1209 switch ((page << 8) + subpage) {
1210 case 0x1D00: /* Element Address Assignment */
1211 if (request->datain_len < 20 || cdb[4] < 20)
1212 return scsi_fail_with_sense_code(
1213 sess, reply, SCSI_STATUS_CHECK_CONDITION,
1214 SCSI_SENSE_KEY_ILLEGAL_REQUEST, ASQ_INVALID_FIELD_IN_CDB);
1215
1216 response_len = 24;
1217 p = response = NDMOS_API_MALLOC(response_len);
1218 if (!response) return NDMP9_NO_MEM_ERR;
1219 NDMOS_API_BZERO(response, response_len);
1220 *(p++) = response_len;
1221 *(p++) = 0; /* reserved medium type */
1222 *(p++) = 0; /* reserved device-specific parameter */
1223 *(p++) = 0; /* block descriptor length (DBD = 0 above)*/
1224 *(p++) = 0x1D; /* page code */
1225 *(p++) = 18; /* remaining bytes */
1226 *(p++) = (MTE_FIRST >> 8) & 0xff;
1227 *(p++) = MTE_FIRST & 0xff;
1228 *(p++) = (MTE_COUNT >> 8) & 0xff;
1229 *(p++) = MTE_COUNT & 0xff;
1230 *(p++) = (STORAGE_FIRST >> 8) & 0xff;
1231 *(p++) = STORAGE_FIRST & 0xff;
1232 *(p++) = (STORAGE_COUNT >> 8) & 0xff;
1233 *(p++) = STORAGE_COUNT & 0xff;
1234 *(p++) = (IE_FIRST >> 8) & 0xff;
1235 *(p++) = IE_FIRST & 0xff;
1236 *(p++) = (IE_COUNT >> 8) & 0xff;
1237 *(p++) = IE_COUNT & 0xff;
1238 *(p++) = (DTE_FIRST >> 8) & 0xff;
1239 *(p++) = DTE_FIRST & 0xff;
1240 *(p++) = (DTE_COUNT >> 8) & 0xff;
1241 *(p++) = DTE_COUNT & 0xff;
1242 /* remainder is zero */
1243 break;
1244
1245 default:
1246 return scsi_fail_with_sense_code(sess, reply, SCSI_STATUS_CHECK_CONDITION,
1247 SCSI_SENSE_KEY_ILLEGAL_REQUEST,
1248 ASQ_INVALID_FIELD_IN_CDB);
1249 }
1250
1251 reply->datain.datain_len = response_len;
1252 reply->datain.datain_val = response;
1253
1254 return NDMP9_NO_ERR;
1255 }
1256
execute_cdb_read_element_status(struct ndm_session * sess,ndmp9_execute_cdb_request * request,ndmp9_execute_cdb_reply * reply)1257 static ndmp9_error execute_cdb_read_element_status(
1258 struct ndm_session* sess,
1259 ndmp9_execute_cdb_request* request,
1260 ndmp9_execute_cdb_reply* reply)
1261 {
1262 unsigned char* cdb = (unsigned char*)request->cdb.cdb_val;
1263 struct robot_state rs;
1264 int min_addr, max_elts;
1265 char* response;
1266 int response_len;
1267 int required_len;
1268 int num_elts = IE_COUNT + MTE_COUNT + DTE_COUNT + STORAGE_COUNT;
1269 char* p;
1270
1271 if (request->cdb.cdb_len != 12 || request->data_dir != NDMP9_SCSI_DATA_DIR_IN)
1272 return scsi_fail_with_sense_code(sess, reply, SCSI_STATUS_CHECK_CONDITION,
1273 SCSI_SENSE_KEY_ILLEGAL_REQUEST,
1274 ASQ_INVALID_FIELD_IN_CDB);
1275 min_addr = (cdb[2] << 8) + cdb[3];
1276 max_elts = (cdb[4] << 8) + cdb[5];
1277 response_len = (cdb[7] << 16) + (cdb[8] << 8) + cdb[9];
1278
1279 if (response_len < 8) {
1280 return scsi_fail_with_sense_code(sess, reply, SCSI_STATUS_CHECK_CONDITION,
1281 SCSI_SENSE_KEY_ILLEGAL_REQUEST,
1282 ASQ_INVALID_FIELD_IN_CDB);
1283 }
1284
1285 /* this is bogus, but we don't allow "partial" status requests */
1286 if (min_addr > IE_FIRST || max_elts < num_elts) {
1287 return scsi_fail_with_sense_code(sess, reply, SCSI_STATUS_CHECK_CONDITION,
1288 SCSI_SENSE_KEY_ILLEGAL_REQUEST,
1289 ASQ_INVALID_FIELD_IN_CDB);
1290 }
1291
1292 robot_state_load(sess, &rs);
1293 robot_state_save(sess, &rs);
1294
1295 /* calculate the total space required */
1296 required_len = 8; /* element status data header */
1297 if (MTE_COUNT) {
1298 required_len += 8; /* element status page header */
1299 required_len += 12 * MTE_COUNT; /* element status descriptor w/o tags */
1300 }
1301 if (STORAGE_COUNT) {
1302 required_len += 8; /* element status page header */
1303 required_len += 84 * STORAGE_COUNT; /* element status descriptor w/ tags */
1304 }
1305 if (IE_COUNT) {
1306 required_len += 8; /* element status page header */
1307 required_len += 84 * IE_COUNT; /* element status descriptor w/ tags */
1308 }
1309 if (DTE_COUNT) {
1310 required_len += 8; /* element status page header */
1311 required_len += 84 * DTE_COUNT; /* element status descriptor w/ tags */
1312 }
1313
1314 p = response = NDMOS_API_MALLOC(response_len);
1315 if (!response) return NDMP9_NO_MEM_ERR;
1316 NDMOS_API_BZERO(response, response_len);
1317
1318 /* write the element status data header */
1319 *(p++) = IE_FIRST >> 8; /* first element address */
1320 *(p++) = IE_FIRST & 0xff;
1321 *(p++) = num_elts >> 8; /* number of elements */
1322 *(p++) = num_elts & 0xff;
1323 *(p++) = 0; /* reserved */
1324 *(p++) = (required_len - 8) >> 16; /* remaining byte count of report */
1325 *(p++) = ((required_len - 8) >> 8) & 0xff;
1326 *(p++) = (required_len - 8) & 0xff;
1327
1328 /* only fill in the rest if we have space */
1329 if (required_len <= response_len) {
1330 int i;
1331 struct {
1332 int first, count, have_voltags, eltype;
1333 int empty_flags, full_flags;
1334 struct element_state* es;
1335 } page[4] = {
1336 {IE_FIRST, IE_COUNT, 1, 3, 0x38, 0x39, &rs.ie[0]},
1337 {MTE_FIRST, MTE_COUNT, 0, 1, 0x00, 0x01, &rs.mte[0]},
1338 {DTE_FIRST, DTE_COUNT, 1, 4, 0x08, 0x81, &rs.dte[0]},
1339 {STORAGE_FIRST, STORAGE_COUNT, 1, 2, 0x08, 0x09, &rs.storage[0]},
1340 };
1341
1342 for (i = 0; i < 4; i++) {
1343 int descr_size = page[i].have_voltags ? 84 : 12;
1344 int totalsize = descr_size * page[i].count;
1345 int j;
1346
1347 if (page[i].count == 0) continue;
1348
1349 /* write the page header */
1350 *(p++) = page[i].eltype;
1351 *(p++) = page[i].have_voltags ? 0xc0 : 0;
1352 *(p++) = 0;
1353 *(p++) = descr_size;
1354 *(p++) = 0; /* reserved */
1355 *(p++) = totalsize >> 16;
1356 *(p++) = (totalsize >> 8) & 0xff;
1357 *(p++) = totalsize & 0xff;
1358
1359 /* and write each descriptor */
1360 for (j = 0; j < page[i].count; j++) {
1361 int elt_addr = page[i].first + j;
1362 int src_elt = page[i].es[j].source_element;
1363 unsigned char byte9 = page[i].es[j].medium_type;
1364 if (src_elt != 0) byte9 |= 0x80; /* SVALID */
1365
1366 *(p++) = elt_addr >> 8;
1367 *(p++) = elt_addr & 0xff;
1368 *(p++) = page[i].es[j].full ? page[i].full_flags : page[i].empty_flags;
1369 *(p++) = 0;
1370 *(p++) = 0;
1371 *(p++) = 0;
1372 *(p++) = 0;
1373 *(p++) = 0;
1374 *(p++) = 0;
1375 *(p++) = byte9;
1376 *(p++) = src_elt >> 8;
1377 *(p++) = src_elt & 0xff;
1378
1379 if (page[i].have_voltags) {
1380 int k;
1381 if (page[i].es[j].full) {
1382 for (k = 0; k < 32; k++) {
1383 if (!page[i].es[j].pvoltag[k]) break;
1384 p[k] = page[i].es[j].pvoltag[k];
1385 }
1386 for (k = 0; k < 32; k++) {
1387 if (!page[i].es[j].avoltag[k]) break;
1388 p[k + 36] = page[i].es[j].avoltag[k];
1389 }
1390 } else {
1391 for (k = 0; k < 32; k++) { p[k] = p[k + 36] = ' '; }
1392 }
1393 p += 72;
1394 }
1395 }
1396 }
1397 }
1398
1399 reply->datain.datain_len = response_len;
1400 reply->datain.datain_val = response;
1401
1402 return NDMP9_NO_ERR;
1403 }
1404
execute_cdb_move_medium(struct ndm_session * sess,ndmp9_execute_cdb_request * request,ndmp9_execute_cdb_reply * reply)1405 static ndmp9_error execute_cdb_move_medium(struct ndm_session* sess,
1406 ndmp9_execute_cdb_request* request,
1407 ndmp9_execute_cdb_reply* reply)
1408 {
1409 unsigned char* cdb = (unsigned char*)request->cdb.cdb_val;
1410 struct robot_state rs;
1411 int mte, src, dest;
1412
1413 if (request->cdb.cdb_len != 12)
1414 return scsi_fail_with_sense_code(sess, reply, SCSI_STATUS_CHECK_CONDITION,
1415 SCSI_SENSE_KEY_ILLEGAL_REQUEST,
1416 ASQ_INVALID_FIELD_IN_CDB);
1417 mte = (cdb[2] << 8) + cdb[3];
1418 src = (cdb[4] << 8) + cdb[5];
1419 dest = (cdb[6] << 8) + cdb[7];
1420
1421 if (!IS_MTE_ADDR(mte))
1422 return scsi_fail_with_sense_code(sess, reply, SCSI_STATUS_CHECK_CONDITION,
1423 SCSI_SENSE_KEY_ILLEGAL_REQUEST,
1424 ASQ_INVALID_ELEMENT_ADDRESS);
1425
1426 robot_state_load(sess, &rs);
1427 if (robot_state_move(sess, &rs, src, dest) < 0)
1428 return scsi_fail_with_sense_code(sess, reply, SCSI_STATUS_CHECK_CONDITION,
1429 SCSI_SENSE_KEY_ILLEGAL_REQUEST,
1430 ASQ_INVALID_ELEMENT_ADDRESS);
1431 robot_state_save(sess, &rs);
1432
1433 return NDMP9_NO_ERR;
1434 }
1435
1436 static struct {
1437 unsigned char cdb_byte;
1438 ndmp9_error (*execute_cdb)(struct ndm_session* sess,
1439 ndmp9_execute_cdb_request* request,
1440 ndmp9_execute_cdb_reply* reply);
1441 } cdb_executors[] = {
1442 {SCSI_CMD_TEST_UNIT_READY, execute_cdb_test_unit_ready},
1443 {SCSI_CMD_INQUIRY, execute_cdb_inquiry},
1444 {SCSI_CMD_MODE_SENSE_6, execute_cdb_mode_sense_6},
1445 {SCSI_CMD_READ_ELEMENT_STATUS, execute_cdb_read_element_status},
1446 {SCSI_CMD_MOVE_MEDIUM, execute_cdb_move_medium},
1447 {0, 0},
1448 };
1449
ndmjob_scsi_open(struct ndm_session * sess,char * name)1450 static ndmp9_error ndmjob_scsi_open(struct ndm_session* sess, char* name)
1451 {
1452 struct stat st;
1453 struct ndm_robot_agent* ra = sess->robot_acb;
1454
1455 // if (!name || strlen(name) > sizeof(ra->sim_dir)-1)
1456 // return NDMP9_NO_DEVICE_ERR;
1457
1458 /* check that it's a directory */
1459 if (stat(name, &st) < 0) return NDMP9_NO_DEVICE_ERR;
1460 if (!S_ISDIR(st.st_mode)) return NDMP9_NO_DEVICE_ERR;
1461
1462 ra->sim_dir = NDMOS_API_STRDUP(name);
1463 ra->scsi_state.error = NDMP9_NO_ERR;
1464
1465 return NDMP9_NO_ERR;
1466 }
1467
ndmjob_scsi_close(struct ndm_session * sess)1468 static ndmp9_error ndmjob_scsi_close(struct ndm_session* sess)
1469 {
1470 return NDMP9_NO_ERR;
1471 }
1472
ndmjob_scsi_reset(struct ndm_session * sess)1473 static ndmp9_error ndmjob_scsi_reset(struct ndm_session* sess)
1474 {
1475 return NDMP9_NO_ERR;
1476 }
1477
ndmjob_scsi_execute_cdb(struct ndm_session * sess,ndmp9_execute_cdb_request * request,ndmp9_execute_cdb_reply * reply)1478 static ndmp9_error ndmjob_scsi_execute_cdb(struct ndm_session* sess,
1479 ndmp9_execute_cdb_request* request,
1480 ndmp9_execute_cdb_reply* reply)
1481 {
1482 unsigned char cdb_byte;
1483 int i;
1484
1485 cdb_byte = request->cdb.cdb_val[0];
1486 for (i = 0; cdb_executors[i].execute_cdb; i++) {
1487 if (cdb_executors[i].cdb_byte == cdb_byte)
1488 return cdb_executors[i].execute_cdb(sess, request, reply);
1489 }
1490
1491 return NDMP9_ILLEGAL_ARGS_ERR;
1492 }
1493 #endif /* NDMOS_OPTION_ROBOT_SIMULATOR */
1494
ndmjob_validate_password(struct ndm_session * sess,char * name,char * pass)1495 static int ndmjob_validate_password(struct ndm_session* sess,
1496 char* name,
1497 char* pass)
1498 {
1499 if (strcmp(name, "ndmp") != 0) return 0;
1500
1501 if (strcmp(pass, "ndmp") != 0) return 0;
1502
1503 return 1; /* OK */
1504 }
1505
ndmjob_validate_md5(struct ndm_session * sess,char * name,char digest[16])1506 static int ndmjob_validate_md5(struct ndm_session* sess,
1507 char* name,
1508 char digest[16])
1509 {
1510 if (strcmp(name, "ndmp") != 0) return 0;
1511
1512 if (!ndmmd5_ok_digest(sess->md5_challenge, "ndmp", digest)) return 0;
1513
1514 return 1; /* OK */
1515 }
1516
ndmjob_register_callbacks(struct ndm_session * sess,struct ndmlog * ixlog)1517 void ndmjob_register_callbacks(struct ndm_session* sess, struct ndmlog* ixlog)
1518 {
1519 #ifdef NDMOS_OPTION_TAPE_SIMULATOR
1520 struct ndm_tape_simulator_callbacks tape_callbacks;
1521 #endif /* NDMOS_OPTION_TAPE_SIMULATOR */
1522 #ifdef NDMOS_OPTION_ROBOT_SIMULATOR
1523 struct ndm_robot_simulator_callbacks robot_callbacks;
1524 #endif /* NDMOS_OPTION_ROBOT_SIMULATOR */
1525 struct ndm_auth_callbacks auth_callbacks;
1526 struct ndm_fhdb_callbacks fhdb_callbacks;
1527
1528 #ifdef NDMOS_OPTION_TAPE_SIMULATOR
1529 tape_callbacks.tape_open = ndmjob_tape_open;
1530 tape_callbacks.tape_close = ndmjob_tape_close;
1531 tape_callbacks.tape_mtio = ndmjob_tape_mtio;
1532 tape_callbacks.tape_write = ndmjob_tape_write;
1533 tape_callbacks.tape_wfm = ndmjob_tape_wfm;
1534 tape_callbacks.tape_read = ndmjob_tape_read;
1535
1536 ndmos_tape_register_callbacks(sess, &tape_callbacks);
1537 #endif /* NDMOS_OPTION_TAPE_SIMULATOR */
1538
1539 #ifdef NDMOS_OPTION_ROBOT_SIMULATOR
1540 robot_callbacks.scsi_open = ndmjob_scsi_open;
1541 robot_callbacks.scsi_close = ndmjob_scsi_close;
1542 robot_callbacks.scsi_reset = ndmjob_scsi_reset;
1543 robot_callbacks.scsi_execute_cdb = ndmjob_scsi_execute_cdb;
1544
1545 ndmos_scsi_register_callbacks(sess, &robot_callbacks);
1546 #endif /* NDMOS_OPTION_ROBOT_SIMULATOR */
1547
1548 auth_callbacks.validate_password = ndmjob_validate_password;
1549 auth_callbacks.validate_md5 = ndmjob_validate_md5;
1550
1551 ndmos_auth_register_callbacks(sess, &auth_callbacks);
1552
1553 fhdb_callbacks.add_file = ndmjobfhdb_add_file;
1554 fhdb_callbacks.add_dir = ndmjobfhdb_add_dir;
1555 fhdb_callbacks.add_node = ndmjobfhdb_add_node;
1556 fhdb_callbacks.add_dirnode_root = ndmjobfhdb_add_dirnode_root;
1557
1558 ndmfhdb_register_callbacks(ixlog, &fhdb_callbacks);
1559 }
1560
ndmjob_unregister_callbacks(struct ndm_session * sess,struct ndmlog * ixlog)1561 void ndmjob_unregister_callbacks(struct ndm_session* sess, struct ndmlog* ixlog)
1562 {
1563 ndmfhdb_unregister_callbacks(ixlog);
1564 ndmos_auth_unregister_callbacks(sess);
1565 #ifdef NDMOS_OPTION_ROBOT_SIMULATOR
1566 ndmos_scsi_unregister_callbacks(sess);
1567 #endif /* NDMOS_OPTION_ROBOT_SIMULATOR */
1568 #ifdef NDMOS_OPTION_TAPE_SIMULATOR
1569 ndmos_auth_unregister_callbacks(sess);
1570 #endif /* NDMOS_OPTION_TAPE_SIMULATOR */
1571 }
1572