1 /*
2 * Copyright (C) 2017 Adam Nielsen <a.nielsen@shikadi.net>
3 *
4 * Test code for mbuffer's tape-end detection option.
5 *
6 * Use:
7 * LD_PRELOAD=$PWD/tapetest.so ./mbuffer -b5 -v5 --tapeaware -o output-test < in
8 *
9 * Only specify one filename beginning with the string "output" as this is the
10 * file that will be intercepted, and there can only be one. write() calls
11 * are passed through unchanged until the number of calls given by
12 * EARLY_END_BLOCK has been reached. Then every second write() will fail with
13 * ENOSPC until FINAL_END_BLOCK calls have succeeded in total. Then write()
14 * will return ENOSPC continually until the next call to open(), when the
15 * call count is reset to zero and the process starts again.
16 *
17 * This emulates the behaviour of an LTO tape, with the alternating write()
18 * failures used by the Linux kernel to signal the imminent end of the tape.
19 * mbuffer closes and reopens the device after a tape change, which this code
20 * picks up in the open() call, resetting the counts to simulate the insertion
21 * of a fresh tape. This allows mbuffer's tape-end-detection code to be
22 * tested without needing an actual tape drive.
23 *
24 * This program is free software: you can redistribute it and/or modify
25 * it under the terms of the GNU General Public License as published by
26 * the Free Software Foundation, either version 3 of the License, or
27 * (at your option) any later version.
28 *
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
33 *
34 * You should have received a copy of the GNU General Public License
35 * along with this program. If not, see <http://www.gnu.org/licenses/>.
36 */
37
38 /* BUG: tapetest.c does not handle block sizes > device size */
39
40 #include "mbconf.h" // defines _GNU_SOURCE
41
42 #ifndef __USE_GNU
43 #define __USE_GNU // for RTLD_NEXT
44 #endif
45 #include <dlfcn.h>
46 #include <errno.h>
47 #include <stdarg.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <unistd.h>
51
52 #define EXPAND(A) A ## 1
53 #define ISEMPTY(A) EXPAND(A)
54
55 #if !defined(LIBC_OPEN) || (ISEMPTY(LIBC_OPEN) == 1)
56 #error name of libc open() could not be determined - test cannot be performed
57 #endif
58
59 #if !defined(LIBC_WRITE) || (ISEMPTY(LIBC_WRITE) == 1)
60 #error name of libc write() could not be determined - test cannot be performed
61 #endif
62
63 /* Block number where we start signalling imminent end of tape */
64 #define EARLY_END_BLOCK 5
65
66 /* Block number where we indicate we have reached the end of the tape */
67 #define FINAL_END_BLOCK 10
68
69 typedef int (*open_func_t)(const char *path, int oflag, ...);
70 typedef ssize_t (*write_func_t)(int filedes, const void *buf, size_t nbyte);
71
72 static int block = 0; /* Current block */
73 static int toggle = 0; /* Used to return ENOSPC for every other write() */
74 static int file = -1; /* File handle we are intercepting */
75 static int opencount = 0; /* Number of calls made to open() */
76 static open_func_t orig_open = 0;
77 static write_func_t orig_write = 0;
78
LIBC_OPEN(const char * path,int oflag,...)79 int LIBC_OPEN(const char *path, int oflag, ...)
80 {
81 int fd;
82 char newpath[256];
83 va_list val;
84 va_start(val,oflag);
85 int mode = va_arg(val,int);
86 va_end(val);
87 if (0 == orig_open) {
88 orig_open = (open_func_t)dlsym(RTLD_NEXT, "open");
89 }
90 if (strncmp(path, "output", 6) == 0) {
91 printf("[INTERCEPT] open: %s", path);
92 /* Opening a file that starts with "output", this is the one we will
93 intercept. */
94
95 /* Add .000, .001, etc. onto the end */
96 sprintf(newpath, "%s.%03d", path, ++opencount);
97 printf(", intercepted and writing as %s", newpath);
98
99 fd = orig_open(newpath, oflag, mode);
100
101 /* Remember this descriptor */
102 file = fd;
103
104 /* Reset the block count to zero every time the file is opened, so that we
105 get another full "tape". */
106 block = 0;
107 toggle = 0;
108 } else {
109 fd = orig_open(path, oflag, mode);
110 }
111 printf("\n");
112 return fd;
113 }
114
115
LIBC_WRITE(int filedes,const void * buf,size_t nbyte)116 ssize_t LIBC_WRITE(int filedes, const void *buf, size_t nbyte)
117 {
118 if (0 == orig_write) {
119 orig_write = (write_func_t)dlsym(RTLD_NEXT, "write");
120 }
121 if (filedes == file) {
122 printf("[INTERCEPT] write(block %d): ", block);
123 if (block >= FINAL_END_BLOCK) {
124 printf("ENOSPC (final)\n");
125 errno = ENOSPC;
126 return -1;
127 } else if (block >= EARLY_END_BLOCK) {
128 toggle++;
129 toggle &= 1;
130 if (toggle) {
131 printf("ENOSPC (early)\n");
132 errno = ENOSPC;
133 return -1;
134 }
135 }
136 printf("OK\n");
137 block++;
138 }
139 return orig_write(filedes, buf, nbyte);
140 }
141
142