klee
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
file-creator.c
Go to the documentation of this file.
1 //===-- file-creator.c ----------------------------------------------------===//
2 //
3 // The KLEE Symbolic Virtual Machine
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "klee-replay.h"
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <errno.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <fcntl.h>
20 #include <termios.h>
21 #include <pty.h>
22 #include <time.h>
23 #include <sys/wait.h>
24 #include <sys/time.h>
25 #include <assert.h>
26 
27 static void create_file(int target_fd,
28  const char *target_name,
29  exe_disk_file_t *dfile,
30  const char *tmpdir);
31 static void check_file(int index, exe_disk_file_t *file);
32 static void delete_file(const char *path, int recurse);
33 
34 
35 #define __STDIN -1
36 #define __STDOUT -2
37 
38 static int create_link(const char *fname,
39  exe_disk_file_t *dfile,
40  const char *tmpdir) {
41  char buf[64];
42  struct stat64 *s = dfile->stat;
43 
44  // XXX Broken, we want this path to be somewhere else most likely.
45  sprintf(buf, "%s.lnk", fname);
46  s->st_mode = (s->st_mode & ~S_IFMT) | S_IFREG;
47  create_file(-1, buf, dfile, tmpdir);
48 
49  int res = symlink(buf, fname);
50  if (res < 0) {
51  perror("symlink");
52  }
53 
54  return open(fname, O_RDWR);
55 }
56 
57 
58 static int create_dir(const char *fname, exe_disk_file_t *dfile,
59  const char *tmpdir) {
60  int res = mkdir(fname, dfile->stat->st_mode);
61  if (res < 0) {
62  perror("mkdir");
63  return -1;
64  }
65  return open(fname, O_RDWR);
66 }
67 
68 double getTime() {
69  struct timeval t;
70  gettimeofday(&t, NULL);
71 
72  return (double) t.tv_sec + ((double) t.tv_usec / 1000000.0);
73 }
74 
76 int wait_for_timeout_or_exit(pid_t pid, const char *name, int *statusp) {
77  char *t = getenv("KLEE_REPLAY_TIMEOUT");
78  int timeout = t ? atoi(t) : 5;
79  double wait = timeout * .5;
80  double start = getTime();
81  fprintf(stderr, "note: %s: waiting %.2fs\n", name, wait);
82  while (getTime() - start < wait) {
83  struct timespec r = {0, 1000000};
84  nanosleep(&r, 0);
85  int res = waitpid(pid, statusp, WNOHANG);
86  if (res==pid)
87  return 1;
88  }
89 
90  return 0;
91 }
92 
93 static int create_char_dev(const char *fname, exe_disk_file_t *dfile,
94  const char *tmpdir) {
95  struct stat64 *s = dfile->stat;
96  unsigned flen = dfile->size;
97  char* contents = dfile->contents;
98 
99  // Assume tty, kinda broken, need an actual device id or something
100  struct termios term, *ts=&term;
101  struct winsize win = { 24, 80, 0, 0 };
102  /* Just copied from my system, munged to match what fields
103  uclibc thinks are there. */
104  ts->c_iflag = 27906;
105  ts->c_oflag = 5;
106  ts->c_cflag = 1215;
107  ts->c_lflag = 35287;
108  ts->c_line = 0;
109  ts->c_cc[0] = '\x03';
110  ts->c_cc[1] = '\x1c';
111  ts->c_cc[2] = '\x7f';
112  ts->c_cc[3] = '\x15';
113  ts->c_cc[4] = '\x04';
114  ts->c_cc[5] = '\x00';
115  ts->c_cc[6] = '\x01';
116  ts->c_cc[7] = '\xff';
117  ts->c_cc[8] = '\x11';
118  ts->c_cc[9] = '\x13';
119  ts->c_cc[10] = '\x1a';
120  ts->c_cc[11] = '\xff';
121  ts->c_cc[12] = '\x12';
122  ts->c_cc[13] = '\x0f';
123  ts->c_cc[14] = '\x17';
124  ts->c_cc[15] = '\x16';
125  ts->c_cc[16] = '\xff';
126  ts->c_cc[17] = '\x0';
127  ts->c_cc[18] = '\x0';
128 
129  {
130  char name[1024];
131  int amaster, aslave;
132  int res = openpty(&amaster, &aslave, name, &term, &win);
133  if (res < 0) {
134  perror("openpty");
135  exit(1);
136  }
137 
138  if (symlink(name, fname) == -1) {
139  fprintf(stderr, "unable to create sym link to tty\n");
140  perror("symlink");
141  }
142 
143  // pty will not be world writeable
144  s->st_mode &= ~02;
145 
146  pid_t pid = fork();
147  if (pid < 0) {
148  perror("fork failed\n");
149  exit(1);
150  } else if (pid == 0) {
151  close(amaster);
152 
153  fprintf(stderr, "note: pty slave: setting raw mode\n");
154  {
155  struct termio mode;
156 
157  int res = ioctl(aslave, TCGETA, &mode);
158  assert(!res);
159  mode.c_iflag = IGNBRK;
160  mode.c_oflag &= ~(OLCUC | ONLCR | OCRNL | ONLRET);
161  mode.c_lflag = 0;
162  mode.c_cc[VMIN] = 1;
163  mode.c_cc[VTIME] = 0;
164  res = ioctl(aslave, TCSETA, &mode);
165  assert(res == 0);
166  }
167 
168  return aslave;
169  } else {
170  unsigned pos = 0;
171  int status;
172  fprintf(stderr, "note: pty master: starting\n");
173  close(aslave);
174 
175  while (pos < flen) {
176  int res = write(amaster, &contents[pos], flen - pos);
177  if (res<0) {
178  if (errno != EINTR) {
179  fprintf(stderr, "note: pty master: write error\n");
180  perror("errno");
181  break;
182  }
183  } else if (res) {
184  fprintf(stderr, "note: pty master: wrote: %d (of %d)\n", res, flen);
185  pos += res;
186  }
187  }
188 
189  if (wait_for_timeout_or_exit(pid, "pty master", &status))
190  goto pty_exit;
191 
192  fprintf(stderr, "note: pty master: closing & waiting\n");
193  close(amaster);
194  while (1) {
195  int res = waitpid(pid, &status, 0);
196  if (res < 0) {
197  if (errno != EINTR)
198  break;
199  } else {
200  break;
201  }
202  }
203 
204  pty_exit:
205  close(amaster);
206  fprintf(stderr, "note: pty master: done\n");
207  process_status(status, 0, "PTY MASTER");
208  }
209  }
210 }
211 
212 static int create_pipe(const char *fname, exe_disk_file_t *dfile,
213  const char *tmpdir) {
214  //struct stat64 *s = dfile->stat;
215  unsigned flen = dfile->size;
216  char* contents = dfile->contents;
217 
218  // XXX what is direction ? need more data
219  pid_t pid;
220  int fds[2];
221  int res = pipe(fds);
222  if (res < 0) {
223  perror("pipe");
224  exit(1);
225  }
226 
227  pid = fork();
228  if (pid < 0) {
229  perror("fork");
230  exit(1);
231  } else if (pid == 0) {
232  close(fds[1]);
233  return fds[0];
234  } else {
235  unsigned pos = 0;
236  int status;
237  fprintf(stderr, "note: pipe master: starting\n");
238  close(fds[0]);
239 
240  while (pos < flen) {
241  int res = write(fds[1], &contents[pos], flen - pos);
242  if (res<0) {
243  if (errno != EINTR)
244  break;
245  } else if (res) {
246  pos += res;
247  }
248  }
249 
250  if (wait_for_timeout_or_exit(pid, "pipe master", &status))
251  goto pipe_exit;
252 
253  fprintf(stderr, "note: pipe master: closing & waiting\n");
254  close(fds[1]);
255  while (1) {
256  int res = waitpid(pid, &status, 0);
257  if (res < 0) {
258  if (errno != EINTR)
259  break;
260  } else {
261  break;
262  }
263  }
264 
265  pipe_exit:
266  close(fds[1]);
267  fprintf(stderr, "note: pipe master: done\n");
268  process_status(status, 0, "PTY MASTER");
269  }
270 }
271 
272 
273 static int create_reg_file(const char *fname, exe_disk_file_t *dfile,
274  const char *tmpdir) {
275  struct stat64 *s = dfile->stat;
276  char* contents = dfile->contents;
277  unsigned flen = dfile->size;
278  unsigned mode = s->st_mode & 0777;
279 
280  //fprintf(stderr, "Creating regular file\n");
281 
282  // Open in RDWR just in case we have to end up using this fd.
283 
284  if (__exe_env.version == 0 && mode == 0)
285  mode = 0644;
286 
287 
288  int fd = open(fname, O_CREAT | O_RDWR, mode);
289  // int fd = open(fname, O_CREAT | O_WRONLY, s->st_mode&0777);
290  if (fd < 0) {
291  fprintf(stderr, "Cannot create file %s\n", fname);
292  exit(1);
293  }
294 
295  int r = write(fd, contents, flen);
296  if (r < 0 || (unsigned) r != flen) {
297  fprintf(stderr, "Cannot write file %s\n", fname);
298  exit(1);
299  }
300 
301  struct timeval tv[2];
302  tv[0].tv_sec = s->st_atime;
303  tv[0].tv_usec = 0;
304  tv[1].tv_sec = s->st_mtime;
305  tv[1].tv_usec = 0;
306  futimes(fd, tv);
307 
308  // XXX: Now what we should do is reopen a new fd with the correct modes
309  // as they were given to the process.
310  lseek(fd, 0, SEEK_SET);
311 
312  return fd;
313 }
314 
315 static int delete_dir(const char *path, int recurse) {
316  if (recurse) {
317  DIR *d = opendir(path);
318  struct dirent *de;
319 
320  if (d) {
321  while ((de = readdir(d))) {
322  if (strcmp(de->d_name, ".")!=0 && strcmp(de->d_name, "..")!=0) {
323  char tmp[PATH_MAX];
324  sprintf(tmp, "%s/%s", path, de->d_name);
325  delete_file(tmp, 0);
326  }
327  }
328 
329  closedir(d);
330  }
331  }
332 
333  if (rmdir(path) == -1) {
334  fprintf(stderr, "Cannot create file %s (exists, is dir, can't remove)\n", path);
335  perror("rmdir");
336  return -1;
337  }
338 
339  return 0;
340 }
341 
342 static void delete_file(const char *path, int recurse) {
343  if (unlink(path) < 0 && errno != ENOENT) {
344  if (errno == EISDIR) {
345  delete_dir(path, 1);
346  } else {
347  fprintf(stderr, "Cannot create file %s (already exists)\n", path);
348  perror("unlink");
349  }
350  }
351 }
352 
353 static void create_file(int target_fd,
354  const char *target_name,
355  exe_disk_file_t *dfile,
356  const char *tmpdir) {
357  struct stat64 *s = dfile->stat;
358  char tmpname[PATH_MAX];
359  const char *target;
360  int fd;
361 
362  assert((target_fd == -1) ^ (target_name == NULL));
363 
364  if (target_name) {
365  target = target_name;
366  } else {
367  sprintf(tmpname, "%s/fd%d", tmpdir, target_fd);
368  target = tmpname;
369  }
370 
371  delete_file(target, 1);
372 
373  // XXX get rid of me once a reasonable solution is found
374  s->st_uid = geteuid();
375  s->st_gid = getegid();
376 
377  if (S_ISLNK(s->st_mode)) {
378  fd = create_link(target, dfile, tmpdir);
379  }
380  else if (S_ISDIR(s->st_mode)) {
381  fd = create_dir(target, dfile, tmpdir);
382  }
383  else if (S_ISCHR(s->st_mode)) {
384  fd = create_char_dev(target, dfile, tmpdir);
385  }
386  else if (S_ISFIFO(s->st_mode) ||
387  (target_fd==0 && (s->st_mode & S_IFMT) == 0)) { // XXX hack
388  fd = create_pipe(target, dfile, tmpdir);
389  }
390  else {
391  fd = create_reg_file(target, dfile, tmpdir);
392  }
393 
394  if (fd >= 0) {
395  if (target_fd != -1) {
396  close(target_fd);
397  if (dup2(fd, target_fd) < 0) {
398  fprintf(stderr, "note: dup2 failed for target: %d\n", target_fd);
399  perror("dup2");
400  }
401  close(fd);
402  } else {
403  // Only worry about 1 vs !1
404  if (s->st_nlink > 1) {
405  char tmp2[PATH_MAX];
406  sprintf(tmp2, "%s/%s.link2", tmpdir, target_name);
407  if (link(target_name, tmp2) < 0) {
408  perror("link");
409  exit(1);
410  }
411  }
412 
413  close(fd);
414  }
415  }
416 }
417 
418 void replay_create_files(exe_file_system_t *exe_fs) {
419  char tmpdir[PATH_MAX];
420  unsigned k;
421 
422  if (!getcwd(tmpdir, PATH_MAX)) {
423  perror("getcwd");
424  exit(1);
425  }
426 
427  strcat(tmpdir, ".temps");
428  delete_file(tmpdir, 1);
429  mkdir(tmpdir, 0755);
430 
431  umask(0);
432  for (k=0; k < exe_fs->n_sym_files; k++) {
433  char name[2];
434  sprintf(name, "%c", 'A' + k);
435  create_file(-1, name, &exe_fs->sym_files[k], tmpdir);
436  }
437 
438  if (exe_fs->sym_stdin)
439  create_file(0, NULL, exe_fs->sym_stdin, tmpdir);
440 
441  if (exe_fs->sym_stdout)
442  create_file(1, NULL, exe_fs->sym_stdout, tmpdir);
443 
444  if (exe_fs->sym_stdin)
445  check_file(__STDIN, exe_fs->sym_stdin);
446 
447  if (exe_fs->sym_stdout)
448  check_file(__STDOUT, exe_fs->sym_stdout);
449 
450  for (k=0; k<exe_fs->n_sym_files; ++k)
451  check_file(k, &exe_fs->sym_files[k]);
452 }
453 
454 static void check_file(int index, exe_disk_file_t *dfile) {
455  struct stat s;
456  int res;
457  char name[32];
458 
459  switch (index) {
460  case __STDIN:
461  strcpy(name, "stdin");
462  res = fstat(0, &s);
463  break;
464  case __STDOUT:
465  strcpy(name, "stdout");
466  res = fstat(1, &s);
467  break;
468  default:
469  name[0] = 'A' + index;
470  name[1] = '\0';
471  res = stat(name, &s);
472  break;
473  }
474 
475  if (res < 0) {
476  fprintf(stderr, "warning: check_file %d: stat failure\n", index);
477  return;
478  }
479 
480  if (s.st_dev != dfile->stat->st_dev) {
481  fprintf(stderr, "warning: check_file %s: dev mismatch: %d vs %d\n",
482  name, (int) s.st_dev, (int) dfile->stat->st_dev);
483  }
484 /* if (s.st_ino != dfile->stat->st_ino) { */
485 /* fprintf(stderr, "warning: check_file %s: ino mismatch: %d vs %d\n", */
486 /* name, (int) s.st_ino, (int) dfile->stat->st_ino); */
487 /* } */
488  if (s.st_mode != dfile->stat->st_mode) {
489  fprintf(stderr, "warning: check_file %s: mode mismatch: %#o vs %#o\n",
490  name, s.st_mode, dfile->stat->st_mode);
491  }
492  if (s.st_nlink != dfile->stat->st_nlink) {
493  fprintf(stderr, "warning: check_file %s: nlink mismatch: %d vs %d\n",
494  name, (int) s.st_nlink, (int) dfile->stat->st_nlink);
495  }
496  if (s.st_uid != dfile->stat->st_uid) {
497  fprintf(stderr, "warning: check_file %s: uid mismatch: %d vs %d\n",
498  name, s.st_uid, dfile->stat->st_uid);
499  }
500  if (s.st_gid != dfile->stat->st_gid) {
501  fprintf(stderr, "warning: check_file %s: gid mismatch: %d vs %d\n",
502  name, s.st_gid, dfile->stat->st_gid);
503  }
504  if (s.st_rdev != dfile->stat->st_rdev) {
505  fprintf(stderr, "warning: check_file %s: rdev mismatch: %d vs %d\n",
506  name, (int) s.st_rdev, (int) dfile->stat->st_rdev);
507  }
508  if (s.st_size != dfile->stat->st_size) {
509  fprintf(stderr, "warning: check_file %s: size mismatch: %d vs %d\n",
510  name, (int) s.st_size, (int) dfile->stat->st_size);
511  }
512  if (s.st_blksize != dfile->stat->st_blksize) {
513  fprintf(stderr, "warning: check_file %s: blksize mismatch: %d vs %d\n",
514  name, (int) s.st_blksize, (int) dfile->stat->st_blksize);
515  }
516  if (s.st_blocks != dfile->stat->st_blocks) {
517  fprintf(stderr, "warning: check_file %s: blocks mismatch: %d vs %d\n",
518  name, (int) s.st_blocks, (int) dfile->stat->st_blocks);
519  }
520 /* if (s.st_atime != dfile->stat->st_atime) { */
521 /* fprintf(stderr, "warning: check_file %s: atime mismatch: %d vs %d\n", */
522 /* name, (int) s.st_atime, (int) dfile->stat->st_atime); */
523 /* } */
524 /* if (s.st_mtime != dfile->stat->st_mtime) { */
525 /* fprintf(stderr, "warning: check_file %s: mtime mismatch: %d vs %d\n", */
526 /* name, (int) s.st_mtime, (int) dfile->stat->st_mtime); */
527 /* } */
528 /* if (s.st_ctime != dfile->stat->st_ctime) { */
529 /* fprintf(stderr, "warning: check_file %s: ctime mismatch: %d vs %d\n", */
530 /* name, (int) s.st_ctime, (int) dfile->stat->st_ctime); */
531 /* } */
532 }
static int delete_dir(const char *path, int recurse)
Definition: file-creator.c:315
static void delete_file(const char *path, int recurse)
Definition: file-creator.c:342
double getTime()
Definition: file-creator.c:68
#define __STDIN
Definition: file-creator.c:35
static int create_link(const char *fname, exe_disk_file_t *dfile, const char *tmpdir)
Definition: file-creator.c:38
int wait_for_timeout_or_exit(pid_t pid, const char *name, int *statusp)
Return true if program exited, false if timed out.
Definition: file-creator.c:76
void replay_create_files(exe_file_system_t *exe_fs)
Definition: file-creator.c:418
static void check_file(int index, exe_disk_file_t *file)
Definition: file-creator.c:454
static int create_reg_file(const char *fname, exe_disk_file_t *dfile, const char *tmpdir)
Definition: file-creator.c:273
static int create_char_dev(const char *fname, exe_disk_file_t *dfile, const char *tmpdir)
Definition: file-creator.c:93
void process_status(int status, time_t elapsed, const char *pfx)
Definition: klee-replay.c:118
static int create_pipe(const char *fname, exe_disk_file_t *dfile, const char *tmpdir)
Definition: file-creator.c:212
static int create_dir(const char *fname, exe_disk_file_t *dfile, const char *tmpdir)
Definition: file-creator.c:58
#define __STDOUT
Definition: file-creator.c:36
static void create_file(int target_fd, const char *target_name, exe_disk_file_t *dfile, const char *tmpdir)
Definition: file-creator.c:353