|
| 1 | + |
| 2 | +/* |
| 3 | + * Copyright (C) Cloud Software Group, Inc. |
| 4 | + * |
| 5 | + * This program is free software; you can redistribute it and/or modify |
| 6 | + * it under the terms of the GNU Lesser General Public License as published |
| 7 | + * by the Free Software Foundation; version 2.1 only. with the special |
| 8 | + * exception on linking described in file LICENSE. |
| 9 | + * |
| 10 | + * This program is distributed in the hope that it will be useful, |
| 11 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | + * GNU Lesser General Public License for more details. |
| 14 | + */ |
| 15 | + |
| 16 | +#undef NDEBUG |
| 17 | +#define DEBUG 1 |
| 18 | + |
| 19 | +#if DEBUG |
| 20 | +#define log(fmt, ...) printf(fmt "\n", ##__VA_ARGS__) |
| 21 | +#else |
| 22 | +#define log(fmt, ...) do {} while(0) |
| 23 | +#endif |
| 24 | + |
| 25 | +// include as first file to make sure header is self contained |
| 26 | +#include "redirect_algo.h" |
| 27 | + |
| 28 | +#include <stdio.h> |
| 29 | +#include <stdlib.h> |
| 30 | +#include <errno.h> |
| 31 | +#include <string.h> |
| 32 | +#include <stdint.h> |
| 33 | +#include <stdbool.h> |
| 34 | +#include <assert.h> |
| 35 | + |
| 36 | +static int fake_close(int fd); |
| 37 | + |
| 38 | +typedef struct { |
| 39 | + bool open; |
| 40 | + bool cloexec; |
| 41 | + char *name; |
| 42 | +} fd; |
| 43 | + |
| 44 | +#define NUM_FDS 4096 |
| 45 | +static fd fds[NUM_FDS]; |
| 46 | + |
| 47 | +static bool |
| 48 | +fake_close_fds_from(int fd_from) |
| 49 | +{ |
| 50 | + for (int fd = fd_from; fd < NUM_FDS; ++fd) |
| 51 | + fake_close(fd); |
| 52 | + |
| 53 | + return true; |
| 54 | +} |
| 55 | + |
| 56 | +#define O_WRONLY 1 |
| 57 | +static int |
| 58 | +fake_open(const char *fn, int dummy) |
| 59 | +{ |
| 60 | + for (int i = 0; i < NUM_FDS; ++i) |
| 61 | + if (!fds[i].open) { |
| 62 | + assert(fds[i].name == NULL); |
| 63 | + fds[i].name = strdup(fn); |
| 64 | + fds[i].open = true; |
| 65 | + fds[i].cloexec = false; |
| 66 | + return i; |
| 67 | + } |
| 68 | + assert(0); |
| 69 | + return -1; |
| 70 | +} |
| 71 | + |
| 72 | +static int |
| 73 | +fake_close(int fd) |
| 74 | +{ |
| 75 | + assert(fd >= 0); |
| 76 | + assert(fd < NUM_FDS); |
| 77 | + if (!fds[fd].open) { |
| 78 | + errno = EBADF; |
| 79 | + return -1; |
| 80 | + } |
| 81 | + fds[fd].open = false; |
| 82 | + free(fds[fd].name); |
| 83 | + fds[fd].name = NULL; |
| 84 | + return 0; |
| 85 | +} |
| 86 | + |
| 87 | +static int |
| 88 | +fake_dup2(int from, int to) |
| 89 | +{ |
| 90 | + assert(from >= 0 && from < NUM_FDS); |
| 91 | + assert(to >= 0 && to < NUM_FDS); |
| 92 | + assert(fds[from].open); |
| 93 | + assert(from != to); |
| 94 | + free(fds[to].name); |
| 95 | + fds[to].open = true; |
| 96 | + fds[to].name = strdup(fds[from].name); |
| 97 | + fds[to].cloexec = false; |
| 98 | + return 0; |
| 99 | +} |
| 100 | + |
| 101 | +static int |
| 102 | +fake_fcntl(int fd) |
| 103 | +{ |
| 104 | + assert(fd >= 0 && fd < NUM_FDS); |
| 105 | + assert(fds[fd].open); |
| 106 | + fds[fd].cloexec = false; |
| 107 | + return 0; |
| 108 | +} |
| 109 | + |
| 110 | +int main(int argc, char **argv) |
| 111 | +{ |
| 112 | + // Input where a given FD goes?? |
| 113 | + // No, not enough, can be duplicated. |
| 114 | + // Numbers >4096 in 2 bytes not file descriptor, |
| 115 | + // (-1 for standard, skip for normal). |
| 116 | + // We should add some random fds. |
| 117 | + enum { MAX_FILE_BUF = 2048 }; |
| 118 | + uint16_t file_buf[MAX_FILE_BUF]; |
| 119 | + size_t read = fread(file_buf, 2, MAX_FILE_BUF, stdin); |
| 120 | + if (read < 3) |
| 121 | + return 0; |
| 122 | + |
| 123 | + static const char standard_names[][8] = { |
| 124 | + "stdin", "stdout", "stderr" |
| 125 | + }; |
| 126 | + int num_mappings = 0; |
| 127 | + uint16_t *num = file_buf; |
| 128 | + mapping mappings[MAX_FILE_BUF]; |
| 129 | + int i = 0; |
| 130 | + for (i = 0; i < 3; ++i) { |
| 131 | + mapping *m = &mappings[num_mappings++]; |
| 132 | + m->uuid = standard_names[i]; |
| 133 | + uint16_t n = *num++; |
| 134 | + m->current_fd = n < NUM_FDS ? n : -1; |
| 135 | + m->wanted_fd = i; |
| 136 | + } |
| 137 | + for (; i < read; ++i) { |
| 138 | + uint16_t n = *num++; |
| 139 | + if (n >= NUM_FDS) |
| 140 | + continue; |
| 141 | + |
| 142 | + mapping *m = &mappings[num_mappings++]; |
| 143 | + m->current_fd = n; |
| 144 | + m->wanted_fd = -1; |
| 145 | + char buf[64]; |
| 146 | + sprintf(buf, "file%d", i); |
| 147 | + m->uuid = strdup(buf); |
| 148 | + } |
| 149 | + if (num_mappings > MAX_TOTAL_MAPPINGS) |
| 150 | + return 0; |
| 151 | + |
| 152 | + for (unsigned n = 0; n < num_mappings; ++n) { |
| 153 | + mapping *m = &mappings[n]; |
| 154 | + int fd = m->current_fd; |
| 155 | + if (fd < 0) |
| 156 | + continue; |
| 157 | + fake_close(fd); |
| 158 | + fds[fd].open = true; |
| 159 | + fds[fd].name = strdup(m->uuid); |
| 160 | + fds[fd].cloexec = true; |
| 161 | + } |
| 162 | + |
| 163 | + // Check in the final file mapping all valid mappings |
| 164 | + // have an open file descriptor. |
| 165 | + // There should be no duplicate numbers in current_fd. |
| 166 | + // current_fd must be in a range. |
| 167 | + // Only if wanted_fd >= 0 current_fd can be -1. |
| 168 | + // There should be a correspondance between input and output names. |
| 169 | + // If current_fd was -1 it will still be -1. |
| 170 | + // If wanted_fd >= 0 current_fd should be the same. |
| 171 | + |
| 172 | + fd_operation operations[MAX_OPERATIONS]; |
| 173 | + int num_operations = |
| 174 | + redirect_mappings(mappings, num_mappings, operations); |
| 175 | + assert(num_operations > 0); |
| 176 | + assert(num_operations <= MAX_OPERATIONS); |
| 177 | + |
| 178 | + for (int i = 0; i < num_operations; ++i) { |
| 179 | + const fd_operation* op = &operations[i]; |
| 180 | + log("op %d %d %d", op->fd_from, op->fd_to, op->operation); |
| 181 | + switch (op->operation) { |
| 182 | + case FD_OP_DUP: |
| 183 | + if (op->fd_from == op->fd_to) |
| 184 | + fake_fcntl(op->fd_from); |
| 185 | + else |
| 186 | + fake_dup2(op->fd_from, op->fd_to); |
| 187 | + break; |
| 188 | + case FD_OP_MOVE: |
| 189 | + assert(op->fd_from != op->fd_to); |
| 190 | + fake_dup2(op->fd_from, op->fd_to); |
| 191 | + fake_close(op->fd_from); |
| 192 | + break; |
| 193 | + case FD_OP_DEVNULL: |
| 194 | + // first close old, then create new one |
| 195 | + fake_close(op->fd_to); |
| 196 | + // TODO ideally we want read only for input for Ocaml did the same... |
| 197 | + assert(fake_open("/dev/null", O_WRONLY) == op->fd_to); |
| 198 | + break; |
| 199 | + case FD_OP_CLOSE_FROM: |
| 200 | + fake_close_fds_from(op->fd_from); |
| 201 | + break; |
| 202 | + default: |
| 203 | + assert(0); |
| 204 | + } |
| 205 | + } |
| 206 | + |
| 207 | + // check files opened |
| 208 | + for (int fd = 0; fd < NUM_FDS; ++fd) |
| 209 | + assert(fds[fd].open == (fd < num_mappings)); |
| 210 | + |
| 211 | + for (int fd = 0; fd < num_mappings; ++fd) { |
| 212 | + assert(fds[fd].cloexec == false); |
| 213 | + log("file %d %s", fd, fds[fd].name); |
| 214 | + } |
| 215 | + |
| 216 | + // Check in the final file mapping all valid mappings |
| 217 | + // has an open file descriptor. |
| 218 | + bool already_found[NUM_FDS] = { false, }; |
| 219 | + for (unsigned n = 0; n < num_mappings; ++n) { |
| 220 | + const int fd = mappings[n].current_fd; |
| 221 | + const int wanted = mappings[n].wanted_fd; |
| 222 | + if (fd >= 0) { |
| 223 | + assert(fd < NUM_FDS); |
| 224 | + assert(fds[fd].open); |
| 225 | + |
| 226 | + // There should be no duplicate numbers in current_fd. |
| 227 | + assert(!already_found[fd]); |
| 228 | + already_found[fd] = true; |
| 229 | + } else { |
| 230 | + // Only if wanted_fd >= 0 current_fd can be -1. |
| 231 | + assert(mappings[n].wanted_fd >= 0); |
| 232 | + assert(fd == -1); |
| 233 | + } |
| 234 | + |
| 235 | + // If wanted_fd >= 0 current_fd should be the same. |
| 236 | + if (wanted >= 0) |
| 237 | + assert(wanted == fd || fd == -1); |
| 238 | + |
| 239 | + // current_fd must be in a range. |
| 240 | + assert(fd >= -1); |
| 241 | + assert(fd < num_mappings); |
| 242 | + } |
| 243 | + |
| 244 | + // There should be a correspondance between input and output names. |
| 245 | + // If current_fd was -1 it will still be -1. |
| 246 | +} |
0 commit comments