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