Imported Upstream version 3.3.0
[debian/amanda] / ndmp-src / amndma_tape_simulator.c
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:
34  *
35  */
36
37
38 #include "ndmagents.h"
39
40
41 #ifndef NDMOS_OPTION_NO_TAPE_AGENT
42
43
44 int     simu_back_one (struct ndm_session *sess, int over_file_mark);
45 int     simu_forw_one (struct ndm_session *sess, int over_file_mark);
46 int     simu_flush_weof (struct ndm_session *sess);
47
48
49 #ifdef NDMOS_OPTION_TAPE_SIMULATOR
50
51 #define SIMU_GAP_MAGIC          0x0BEEFEE0
52 #define SIMU_GAP_RT_(a,b,c,d) ((a<<0)+(b<<8)+(c<<16)+(d<<24))
53 #define SIMU_GAP_RT_BOT         SIMU_GAP_RT_('B','O','T','_')
54 #define SIMU_GAP_RT_DATA        SIMU_GAP_RT_('D','A','T','A')
55 #define SIMU_GAP_RT_FILE        SIMU_GAP_RT_('F','I','L','E')
56 #define SIMU_GAP_RT_EOT         SIMU_GAP_RT_('E','O','T','_')
57
58
59
60 int
61 ndmos_tape_initialize (struct ndm_session *sess)
62 {
63         struct ndm_tape_agent * ta = &sess->tape_acb;
64
65         ta->tape_fd = -1;
66         NDMOS_MACRO_ZEROFILL (&ta->tape_state);
67         ta->tape_state.error = NDMP9_DEV_NOT_OPEN_ERR;
68         ta->tape_state.state = NDMP9_TAPE_STATE_IDLE;
69
70         return 0;
71 }
72
73 ndmp9_error
74 ndmos_tape_open (struct ndm_session *sess, char *drive_name, int will_write)
75 {
76         struct ndm_tape_agent * ta = &sess->tape_acb;
77         struct stat             st;
78         int                     read_only, omode;
79         int                     fd;
80
81         if (ta->tape_fd >= 0) {
82                 return NDMP9_DEVICE_OPENED_ERR;
83         }
84
85         if (*drive_name >= '0' && *drive_name <= '9') {
86                 fd = atoi(drive_name);
87                 goto skip_header_check;
88         }
89
90         if (stat (drive_name, &st) < 0) {
91                 return NDMP9_NO_DEVICE_ERR;
92         }
93
94         read_only = (st.st_mode & 0222) == 0;
95
96         if (!will_write) {
97                 omode = 0;
98         } else {
99                 if (read_only)
100                         return NDMP9_WRITE_PROTECT_ERR;
101                 omode = 2;              /* ndmp_write means read/write */
102         }
103
104         fd = open (drive_name, omode);
105         if (fd < 0) {
106                 return NDMP9_PERMISSION_ERR;
107         }
108
109         if (st.st_size == 0) {
110                 if (will_write) {
111                         lseek (fd, (off_t)0, 0);
112                 } else {
113                         goto skip_header_check;
114                 }
115         }
116
117   skip_header_check:
118         ta->tape_fd = fd;
119         NDMOS_API_BZERO (ta->drive_name, sizeof ta->drive_name);
120         strcpy (ta->drive_name, drive_name);
121         bzero (&ta->tape_state, sizeof ta->tape_state);
122         ta->tape_state.error = NDMP9_NO_ERR;
123         ta->tape_state.state = NDMP9_TAPE_STATE_OPEN;
124         ta->tape_state.open_mode =
125                 will_write ? NDMP9_TAPE_RDWR_MODE : NDMP9_TAPE_READ_MODE;
126         ta->tape_state.file_num.valid = NDMP9_VALIDITY_VALID;
127         ta->tape_state.soft_errors.valid = NDMP9_VALIDITY_VALID;
128         ta->tape_state.block_size.valid = NDMP9_VALIDITY_VALID;
129         ta->tape_state.blockno.valid = NDMP9_VALIDITY_VALID;
130         ta->tape_state.total_space.valid = NDMP9_VALIDITY_INVALID;
131         ta->tape_state.space_remain.valid = NDMP9_VALIDITY_INVALID;
132
133         return NDMP9_NO_ERR;
134 }
135
136 ndmp9_error
137 ndmos_tape_close (struct ndm_session *sess)
138 {
139         struct ndm_tape_agent * ta = &sess->tape_acb;
140
141         if (ta->tape_fd < 0) {
142                 return NDMP9_DEV_NOT_OPEN_ERR;
143         }
144
145         simu_flush_weof(sess);
146
147 #if 0
148         u_long                  resid;
149         ndmos_tape_mtio (sess, NDMP9_MTIO_REW, 1, &resid);
150 #endif
151
152         close (ta->tape_fd);
153         ta->tape_fd = -1;
154
155         ndmos_tape_initialize (sess);
156
157         return NDMP9_NO_ERR;
158 }
159
160 void
161 ndmos_tape_sync_state (struct ndm_session *sess)
162 {
163         struct ndm_tape_agent * ta = &sess->tape_acb;
164
165         if (ta->tape_fd < 0) {
166                 ta->tape_state.error = NDMP9_DEV_NOT_OPEN_ERR;
167                 ta->tape_state.state = NDMP9_TAPE_STATE_IDLE;
168                 ta->tape_state.file_num.valid = NDMP9_VALIDITY_INVALID;
169                 ta->tape_state.soft_errors.valid = NDMP9_VALIDITY_INVALID;
170                 ta->tape_state.block_size.valid = NDMP9_VALIDITY_INVALID;
171                 ta->tape_state.blockno.valid = NDMP9_VALIDITY_INVALID;
172                 ta->tape_state.total_space.valid = NDMP9_VALIDITY_INVALID;
173                 ta->tape_state.space_remain.valid = NDMP9_VALIDITY_INVALID;
174         } else {
175                 ta->tape_state.error = NDMP9_NO_ERR;
176                 if (ta->mover_state.state == NDMP9_MOVER_STATE_ACTIVE)
177                         ta->tape_state.state = NDMP9_TAPE_STATE_MOVER;
178                 else
179                         ta->tape_state.state = NDMP9_TAPE_STATE_OPEN;
180                 ta->tape_state.file_num.valid = NDMP9_VALIDITY_VALID;
181                 ta->tape_state.soft_errors.valid = NDMP9_VALIDITY_VALID;
182                 ta->tape_state.block_size.valid = NDMP9_VALIDITY_VALID;
183                 ta->tape_state.blockno.valid = NDMP9_VALIDITY_VALID;
184                 ta->tape_state.total_space.valid = NDMP9_VALIDITY_INVALID;
185                 ta->tape_state.space_remain.valid = NDMP9_VALIDITY_INVALID;
186         }
187
188         return;
189 }
190
191 int
192 simu_back_one (struct ndm_session *sess, int over_file_mark)
193 {
194         struct ndm_tape_agent * ta = &sess->tape_acb;
195         off_t                   cur_pos;
196
197         cur_pos = lseek (ta->tape_fd, (off_t)0, 1);
198         lseek (ta->tape_fd, 0, 0);
199 return 0;
200 }
201
202 int
203 simu_forw_one (struct ndm_session *sess, int over_file_mark)
204 {
205         struct ndm_tape_agent * ta = &sess->tape_acb;
206         off_t                   cur_pos;
207
208         cur_pos = lseek (ta->tape_fd, (off_t)0, 1);
209 return 0;
210
211 }
212
213 int
214 simu_flush_weof (struct ndm_session *sess)
215 {
216         struct ndm_tape_agent * ta = &sess->tape_acb;
217
218         if (ta->weof_on_close) {
219                 /* best effort */
220                 ndmos_tape_wfm (sess);
221         }
222         return 0;
223 }
224
225
226 ndmp9_error
227 ndmos_tape_mtio (struct ndm_session *sess,
228   ndmp9_tape_mtio_op op, u_long count, u_long *resid)
229 {
230         struct ndm_tape_agent * ta = &sess->tape_acb;
231         int                     rc;
232
233         *resid = 0;
234
235         if (ta->tape_fd < 0) {
236                 return NDMP9_DEV_NOT_OPEN_ERR;
237         }
238
239         
240         /* audit for valid op and for tape mode */
241         switch (op) {
242         case NDMP9_MTIO_FSF:
243                 return NDMP9_NO_ERR;
244                 while (*resid > 0) {
245                         simu_flush_weof(sess);
246                         rc = simu_forw_one (sess, 1);
247                         if (rc < 0)
248                                 return NDMP9_IO_ERR;
249                         if (rc == 0)
250                                 break;
251                         if (rc == SIMU_GAP_RT_FILE)
252                                 *resid -= 1;
253                 }
254                 break;
255
256         case NDMP9_MTIO_BSF:
257                 return NDMP9_NO_ERR;
258                 while (*resid > 0) {
259                         simu_flush_weof(sess);
260                         rc = simu_back_one (sess, 1);
261                         if (rc < 0)
262                                 return NDMP9_IO_ERR;
263                         if (rc == 0)
264                                 break;
265                         if (rc == SIMU_GAP_RT_FILE)
266                                 *resid -= 1;
267                 }
268                 break;
269
270         case NDMP9_MTIO_FSR:
271                 return NDMP9_NO_ERR;
272                 while (*resid > 0) {
273                         simu_flush_weof(sess);
274                         rc = simu_forw_one (sess, 0);
275                         if (rc < 0)
276                                 return NDMP9_IO_ERR;
277                         if (rc == 0)
278                                 break;
279                         *resid -= 1;
280                 }
281                 break;
282
283         case NDMP9_MTIO_BSR:
284                 return NDMP9_NO_ERR;
285                 while (*resid > 0) {
286                         simu_flush_weof(sess);
287                         rc = simu_back_one (sess, 0);
288                         if (rc < 0)
289                                 return NDMP9_IO_ERR;
290                         if (rc == 0)
291                                 break;
292                         *resid -= 1;
293                 }
294                 break;
295
296         case NDMP9_MTIO_REW:
297                 simu_flush_weof(sess);
298                 *resid = 0;
299                 ta->tape_state.file_num.value = 0;
300                 ta->tape_state.blockno.value = 0;
301                 //lseek (ta->tape_fd, (off_t)(sizeof (struct simu_gap)), 0);
302                 lseek (ta->tape_fd, (off_t)0, 0);
303                 ndmalogf(sess, 0, 7, "NDMP9_MTIO_REW");
304                 sleep(1);
305                 break;
306
307         case NDMP9_MTIO_OFF:
308                 return NDMP9_NO_ERR;
309                 simu_flush_weof(sess);
310                 /* Hmmm. */
311                 break;
312
313         case NDMP9_MTIO_EOF:            /* should be "WFM" write-file-mark */
314                 return NDMP9_NO_ERR;
315                 if (!NDMTA_TAPE_IS_WRITABLE(ta)) {
316                         return NDMP9_PERMISSION_ERR;
317                 }
318                 while (*resid > 0) {
319                         ndmp9_error     err;
320
321                         err = ndmos_tape_wfm (sess);
322                         if (err != NDMP9_NO_ERR)
323                                 return err;
324
325                         *resid -= 1;
326                 }
327                 break;
328
329         default:
330                 return NDMP9_ILLEGAL_ARGS_ERR;
331         }
332
333         return NDMP9_NO_ERR;
334 }
335
336 ndmp9_error
337 ndmos_tape_write (struct ndm_session *sess,
338   char *buf, u_long count, u_long *done_count)
339 {
340         struct ndm_tape_agent * ta = &sess->tape_acb;
341         off_t                   cur_pos;
342         ndmp9_error             err;
343         u_long                  prev_size;
344         int                     rc;
345
346         if (ta->tape_fd < 0) {
347                 return NDMP9_DEV_NOT_OPEN_ERR;
348         }
349
350         if (!NDMTA_TAPE_IS_WRITABLE(ta)) {
351                 return NDMP9_PERMISSION_ERR;
352         }
353
354         if (count == 0) {
355                 /*
356                  * NDMPv4 clarification -- a tape read or write with
357                  * a count==0 is a no-op. This is undoubtedly influenced
358                  * by the SCSI Sequential Access specification which
359                  * says much the same thing.
360                  */
361                 *done_count = 0;
362                 return NDMP9_NO_ERR;
363         }
364
365         cur_pos = lseek (ta->tape_fd, (off_t)0, 1);
366         lseek (ta->tape_fd, cur_pos, 0);
367
368         if ((u_long)full_write (ta->tape_fd, buf, count) == count) {
369                 cur_pos += count;
370
371                 prev_size = count;
372
373                 ta->tape_state.blockno.value++;
374
375                 *done_count = count;
376
377                 err = NDMP9_NO_ERR;
378         } else {
379                 ndmalogf(sess, 0, 7, "full_write not %d", count);
380                 err = NDMP9_IO_ERR;
381         }
382
383         /* error ignored for pipe file descriptor */
384         rc = ftruncate (ta->tape_fd, cur_pos);
385
386         lseek (ta->tape_fd, cur_pos, 0);
387
388         ta->weof_on_close = 1;
389
390         return err;
391 }
392
393 ndmp9_error
394 ndmos_tape_wfm (struct ndm_session *sess)
395 {
396         struct ndm_tape_agent * ta = &sess->tape_acb;
397         off_t                   cur_pos;
398         ndmp9_error             err;
399         int                     rc;
400
401         ta->weof_on_close = 0;
402
403         if (ta->tape_fd < 0) {
404                 return NDMP9_DEV_NOT_OPEN_ERR;
405         }
406
407         if (!NDMTA_TAPE_IS_WRITABLE(ta)) {
408                 return NDMP9_PERMISSION_ERR;
409         }
410
411         cur_pos = lseek (ta->tape_fd, (off_t)0, 1);
412
413         lseek (ta->tape_fd, cur_pos, 0);
414         err = NDMP9_NO_ERR;
415
416         /* error ignored for pipe file descriptor */
417         rc = ftruncate (ta->tape_fd, cur_pos);
418
419         lseek (ta->tape_fd, cur_pos, 0);
420
421         return err;
422 }
423
424 ndmp9_error
425 ndmos_tape_read (struct ndm_session *sess,
426   char *buf, u_long count, u_long *done_count)
427 {
428         struct ndm_tape_agent * ta = &sess->tape_acb;
429         int                     rc;
430         unsigned        nb;
431
432         if (ta->tape_fd < 0) {
433                 return NDMP9_DEV_NOT_OPEN_ERR;
434         }
435
436         if (count == 0) {
437                 /*
438                  * NDMPv4 clarification -- a tape read or write with
439                  * a count==0 is a no-op. This is undoubtedly influenced
440                  * by the SCSI Sequential Access specification which
441                  * says much the same thing.
442                  */
443
444                 *done_count = 0;
445                 return NDMP9_NO_ERR;
446         }
447
448         nb = count;
449
450         rc = full_read (ta->tape_fd, buf, nb);
451         if (rc < 0) {
452                 return NDMP9_IO_ERR;
453         }
454         ta->tape_state.blockno.value++;
455
456         *done_count = rc;
457
458         if (rc == 0) {
459                 return NDMP9_EOF_ERR;
460         }
461         return NDMP9_NO_ERR;
462 }
463
464 #endif /* NDMOS_OPTION_TAPE_SIMULATOR */
465
466 #endif /* !NDMOS_OPTION_NO_TAPE_AGENT */