/* * Berkeley Lab Checkpoint/Restart (BLCR) for Linux is Copyright (c) * 2003, The Regents of the University of California, through Lawrence * Berkeley National Laboratory (subject to receipt of any required * approvals from the U.S. Dept. of Energy). All rights reserved. * * Portions may be copyrighted by others, as may be noted in specific * copyright notices within specific files. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Id: filedescriptors.c,v 1.23 2008/08/29 21:36:53 phargrov Exp $ * * Simple example for using crut */ /* * >KEYS: < open() * >WHAT: < Does a read on a file opened with oflag = O_WRONLY return -1? * < Does a write on a file opened with oflag = O_RDONLY return -1? * >HOW: < Open a file with O_WRONLY, test for -1 to pass * < Open a file with O_RDONLY, test for -1 to pass * >BUGS: < DUMMY functions used in BSD * >AUTHOR:< PERENNIAL */ /* * seek tests: * * writes pattern to a file * seek backwards * checkpoint * assert fd->offset == lseek() * read * check pattern * * fill a file with zeros * seek to start * checkpoint * assert fd->offset == lseek() * write pattern * read * check pattern */ #define _LARGEFILE64_SOURCE 1 /* For O_LARGEFILE */ #include #include #include #include #include #include #include #include #include #include "crut.h" #define DEFAULT_TEST_DATA_LENGTH 8192 #define MAX_TEST_FILENAME 255 #define MAX_TEST_FILES 32 #define TEST_PREFIX "tstfile" #define TEST_FILE_MODE 0660 #define TEST_FILE_MODE_RO 0440 #define BUF_SIZE 4096 struct fd_struct { int fd; int flags; off_t offset; struct stat stat; int unlinked; char filename[MAX_TEST_FILENAME+1]; }; int test_flags[] = { #ifdef O_DIRECT O_DIRECT, #endif #ifdef O_LARGEFILE O_LARGEFILE, #endif O_SYNC, O_NONBLOCK, O_NDELAY, O_APPEND, }; /* * Linux 2.4 does not save these flags. So we assume that they're not going * to be saved, and test for them later when you talk to fcntl. */ #define FLAGS_NOT_RESTORED (O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC) /* * 64-bit kernels are some what "free" with O_LARGEFILE since it is just a * no-op for sizeof(long) == 8 */ #if O_LARGEFILE #define FLAGS_TO_IGNORE O_LARGEFILE #else #define FLAGS_TO_IGNORE 0 #endif /* * write_pattern * * write a test pattern to this file descriptor * * note: * write_pattern(f, len1); * write_pattern(f, len2); * * should produce the same output as * write_pattern(f, len1+len2); */ static int write_pattern(int fd, int length) { int retval = -1; off_t seed; char *buf; seed = lseek(fd, 0, SEEK_CUR); if (seed < 0) { goto out; } buf = pattern_get_data(length, seed); if (buf == NULL) { goto out; } retval = write(fd, buf, length); if (retval < 0) { perror("write"); } free(buf); out: return retval; } /* * check_pattern * * checks that length bytes in fd match our test pattern */ static int check_pattern(int fd, int length) { int retval = -1; int seed; char *buf; seed = lseek(fd, 0, SEEK_CUR); if (seed < 0) { perror("lseek"); goto out; } buf = malloc(length); if (buf == NULL) { perror("malloc"); goto out; } retval = read(fd, buf, length); if (retval < 0) { perror("read"); goto out_free; } retval = pattern_compare(buf, length, seed); if (retval < 0) { CRUT_FAIL("File failed to match expected contents at recovery."); } out_free: free(buf); out: return retval; } static int check_flags(struct fd_struct *f) { int retval = -1; int flags; /* check the flags */ flags = fcntl(f->fd, F_GETFL); if (flags < 0) { perror("fcntl"); return flags; } flags &= ~FLAGS_TO_IGNORE; if (flags != f->flags) { CRUT_FAIL("Flag 0%o from fcntl does not match saved value of 0%o", flags, f->flags); } else { retval = 0; } /* * Linux 2.4 does not save certain flags. So we assume that they're not * going to be saved, and test for them. * * All sorts of things will break if this ever happens... */ if (flags & (FLAGS_NOT_RESTORED)) { CRUT_FAIL("Kernel flag behavior changed!!!"); retval = -1; } return retval; } /* * make sure the file pointer was correctly positioned */ static int check_offset(struct fd_struct *f) { int retval = -1; int offset; offset = lseek(f->fd, 0, SEEK_CUR); if (offset != f->offset) { CRUT_FAIL("File pointer positioned incorrectly at recovery."); } else if (offset < 0) { perror("lseek"); } else { retval = 0; } return retval; } /* * check_stat_simple * checks basic stats on a regular file... */ static int check_stat_simple(struct fd_struct *f) { int retval = -1; struct stat new_stat; /* compare the stat structs */ retval = fstat(f->fd, &new_stat); if (retval < 0) { perror("fstat"); } else { retval = statcmp(&new_stat, &(f->stat), ST_SIZE | ST_MODE | ST_NLINK); if (retval) { CRUT_FAIL("File attributes changed. %d mismatches", retval); CRUT_DEBUG("--- Old stats %p ---", &(f->stat)); dump_stat(&(f->stat)); CRUT_DEBUG("--- Current stats ---"); dump_stat(&new_stat); retval = -1; } } return retval; } static int create_temp_file(const char *filename, int length) { int fd; int retval = -1; (void)unlink(filename); fd = open(filename, O_WRONLY | O_CREAT, TEST_FILE_MODE); if (fd < 0) { perror("create_temp_file(open)"); retval = fd; } else { retval = write_pattern(fd, length); (void)close(fd); } return retval; } /* * fills out the fields in an fd_struct... */ static int update_fd_struct(struct fd_struct *f) { int retval; f->offset = lseek(f->fd, 0, SEEK_CUR); if (f->offset < 0) { retval = -1; perror("lseek"); goto out; } f->flags = fcntl(f->fd, F_GETFL); if (f->flags < 0) { retval = -1; perror("fcntl"); goto out; } f->flags &= ~FLAGS_TO_IGNORE; retval = fstat(f->fd, &f->stat); if (retval < 0) { perror("fstat"); } out: return retval; } /* * open up a file */ static struct fd_struct * open_fd_struct(char *filename, int flags, int do_unlink) { struct fd_struct *f; int retval; f = malloc(sizeof(*f)); if (f==NULL) { perror("malloc"); goto out; } memset(f, 0, sizeof(*f)); strncpy(f->filename, filename, MAX_TEST_FILENAME); f->filename[MAX_TEST_FILENAME] = '\0'; if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) { (void)unlink(f->filename); } f->fd = open(f->filename, flags, TEST_FILE_MODE); if (f->fd < 0) { perror("open"); retval = f->fd; goto out_free; } retval = update_fd_struct(f); if (retval < 0) { goto out_free; } f->unlinked = do_unlink; if (do_unlink) { retval = unlink(filename); if (retval < 0) { perror("unlink"); goto out_free; } } out: return f; out_free: free(f); return NULL; } /* * close a file */ static int close_fd_struct(struct fd_struct *f) { int retval; retval = close(f->fd); if (retval < 0) { perror("close"); } free(f); return retval; } /* * flex our muscles, do some basic things to an open file. * read, write, lseek, stat */ static int check_fd_struct_rdwr(struct fd_struct *f) { int retval; /* check the flags */ retval = check_flags(f); if (retval < 0) { goto out; } CRUT_DEBUG("check_flags:return = %d", retval); /* make sure the file pointer was correctly positioned */ retval = check_offset(f); if (retval < 0) { goto out; } CRUT_DEBUG("check_offset:return = %d", retval); /* compare the stat structs */ retval = check_stat_simple(f); if (retval < 0) { goto out; } CRUT_DEBUG("check_stat_simple:return = %d", retval); /* write some stuff to the file. */ retval = write_pattern(f->fd, DEFAULT_TEST_DATA_LENGTH); if (retval < 0) { goto out; } CRUT_DEBUG("write_pattern:return = %d", retval); /* seek back to beginning */ retval = lseek(f->fd, 0, SEEK_SET); if (retval < 0) { perror("lseek"); goto out; } /* match the file contents to expected */ retval = check_pattern(f->fd, 2*DEFAULT_TEST_DATA_LENGTH); if (retval < 0) { goto out; } /* truncate back to original length */ retval = ftruncate(f->fd, DEFAULT_TEST_DATA_LENGTH); if (retval < 0) { goto out; } out: return retval; } static int _fd_read_setup(void **testdata, int do_unlink) { int length = DEFAULT_TEST_DATA_LENGTH; int retval; struct fd_struct *f = NULL; char *filename = crut_aprintf("%s.%d", TEST_PREFIX, 0); retval = create_temp_file(filename, length); if (retval < 0) { goto out; } f = open_fd_struct(filename, O_RDONLY, do_unlink); if (f == NULL) { retval = -1; goto out; } retval = lseek(f->fd, 1, SEEK_CUR); if (retval < 0) { perror("lseek"); goto out; } retval = fchmod(f->fd, TEST_FILE_MODE_RO); if (retval < 0) { perror("fchmod"); goto out; } out: *testdata = f; return 0; } static int fd_read_setup(void **testdata) { return _fd_read_setup(testdata, 0); } static int fd_read_unlinked_setup(void **testdata) { return _fd_read_setup(testdata, 1); } /* * We create a new file for writing. After restart, we'll write to it. */ static int _fd_write_setup(void **testdata, int do_unlink) { struct fd_struct *f; char *filename; filename = crut_aprintf("%s.%d", TEST_PREFIX, 0); f = open_fd_struct(filename, O_WRONLY | O_CREAT | O_EXCL, do_unlink); free(filename); *testdata = f; if (f == NULL) { return -1; } else { return 0; } } static int fd_write_setup(void **testdata) { return _fd_write_setup(testdata, 0); } static int fd_write_unlinked_setup(void **testdata) { return _fd_write_setup(testdata, 1); } /* * We create a new file opened for read-write. We're going to write * some data into it now. At restart we write some more data in, * then read it all back. */ static int _fd_rdwr_setup(void **testdata, int do_unlink) { int retval = -1; struct fd_struct *f; char *filename; filename = crut_aprintf("%s.%d", TEST_PREFIX, 0); f = open_fd_struct(filename, O_RDWR | O_CREAT | O_EXCL, do_unlink); if (f == NULL) { goto out; } /* write some stuff to the file. */ retval = write_pattern(f->fd, DEFAULT_TEST_DATA_LENGTH); if (retval < 0) { CRUT_FAIL("Unable to write test pattern to file opened O_RDWR"); goto out_close_unlink; } out: *testdata = f; free(filename); return retval; out_close_unlink: close_fd_struct(f); if (!do_unlink && (unlink(filename) < 0)) { perror("unlink"); } free(filename); return retval; } static int fd_rdwr_setup(void **testdata) { return _fd_rdwr_setup(testdata, 0); } static int fd_rdwr_unlinked_setup(void **testdata) { return _fd_rdwr_setup(testdata, 1); } /* * Test open files w/ a bunch of different modes. */ static int _fd_flags_setup(void **testdata, int do_unlink) { int i; int retval = -1; int num_tests = sizeof(test_flags)/sizeof(int); int length = DEFAULT_TEST_DATA_LENGTH; char *filename; struct fd_struct **fds; /* get us some space and init... */ fds = malloc(num_tests*sizeof(struct fd_struct)); if (fds == NULL) { goto out; } for (i=0; iunlinked = do_unlink; if (do_unlink) { retval = unlink(filename); if (retval < 0) { perror("unlink"); goto out_close; } } *testdata = fds; return 0; out_close: /* close anything we opened */ for (i=0; iflags = fds[i]->flags | O_RDWR; } return retval; } static int fd_generic_continue(void *p) { int retval; CRUT_DEBUG("Continuing after checkpoint."); retval = update_fd_struct((struct fd_struct *)p); return retval; } static int fd_read_restart(void *p) { int retval; struct fd_struct *f = (struct fd_struct *)p; CRUT_DEBUG("Restarting from checkpoint."); /* check the flags */ retval = check_flags(f); if (retval < 0) { goto out; } /* make sure the file pointer was correctly positioned */ retval = check_offset(f); if (retval < 0) { goto out; } /* compare the stat structs */ retval = check_stat_simple(f); if (retval < 0) { goto out; } /* match the file contents to expected */ retval = check_pattern(f->fd, DEFAULT_TEST_DATA_LENGTH); if (retval < 0) { goto out; } /* make sure we can't write to one of these things. */ retval = write(f->fd, f, sizeof(*f)); if (retval >= 0) { perror("write"); CRUT_FAIL("Unexpectedly able to write to a read-only file!"); } else if (errno != EBADF) { CRUT_FAIL("Write failed, but for the wrong reason!"); perror("write"); } else { retval = 0; } out: return retval; } static int fd_write_restart(void *p) { int retval; struct fd_struct *f = (struct fd_struct *)p; struct fd_struct *readf; char buf[BUF_SIZE]; /* check the flags */ retval = check_flags(f); if (retval < 0) { goto out; } CRUT_DEBUG("check_flags:return = %d", retval); /* make sure the file pointer was correctly positioned */ retval = check_offset(f); if (retval < 0) { goto out; } CRUT_DEBUG("check_offset:return = %d", retval); /* compare the stat structs */ retval = check_stat_simple(f); if (retval < 0) { goto out; } CRUT_DEBUG("check_stat_simple:return = %d", retval); /* write some stuff to the file. */ retval = write_pattern(f->fd, DEFAULT_TEST_DATA_LENGTH); if (retval < 0) { goto out; } CRUT_DEBUG("write_pattern:return = %d", retval); /* try to read from a file opened O_WRONLY. Better fail */ retval = read(f->fd, buf, sizeof(*f)); if (retval >= 0) { perror("read"); CRUT_FAIL("Unexpectedly able to read from a write-only file!"); } else if (errno != EBADF) { CRUT_FAIL("Read failed, but for the wrong reason!"); perror("read"); } else { retval = 0; } /* skip reopen for the unlinked case */ if (f->unlinked) goto out; /* reopen to check file contents */ readf = open_fd_struct(f->filename, O_RDONLY, 0); if (readf == NULL) { retval = -1; goto out; } p = readf; /* match the file contents to expected */ retval = check_pattern(readf->fd, DEFAULT_TEST_DATA_LENGTH); if (retval < 0) { goto out; } out: return retval; } static int fd_rdwr_restart(void *p) { int retval; struct fd_struct *f = (struct fd_struct *)p; /* check the flags */ retval = check_flags(f); if (retval < 0) { goto out; } CRUT_DEBUG("check_flags:return = %d", retval); /* make sure the file pointer was correctly positioned */ retval = check_offset(f); if (retval < 0) { goto out; } CRUT_DEBUG("check_offset:return = %d", retval); /* compare the stat structs */ retval = check_stat_simple(f); if (retval < 0) { goto out; } CRUT_DEBUG("check_stat_simple:return = %d", retval); /* write some stuff to the file. */ retval = write_pattern(f->fd, DEFAULT_TEST_DATA_LENGTH); if (retval < 0) { goto out; } CRUT_DEBUG("write_pattern:return = %d", retval); /* seek back to beginning */ retval = lseek(f->fd, 0, SEEK_SET); if (retval < 0) { perror("lseek"); goto out; } /* match the file contents to expected */ retval = check_pattern(f->fd, 2*DEFAULT_TEST_DATA_LENGTH); if (retval < 0) { goto out; } out: return retval; } static int fd_flags_restart(void *p) { struct fd_struct **fds = (struct fd_struct **)p; int num_tests = sizeof(test_flags)/sizeof(int); int i; int retval = 0; for (i=0; ifd, test_flags[i]); break; } } return retval; } static int fd_generic_teardown(void *p) { int retval; struct fd_struct *f = (struct fd_struct *)p; retval = unlink(f->filename); if (f->unlinked) { if ((retval >= 0) || (errno != ENOENT)) { perror("duplicate unlink"); retval = -1; } else { retval = 0; } } else if (retval < 0) { perror("unlink"); } free(p); return retval; } static int fd_flags_teardown(void *p) { int retval; struct fd_struct **f = (struct fd_struct **)p; retval = unlink(f[0]->filename); if (f[0]->unlinked) { if ((retval >= 0) || (errno != ENOENT)) { perror("duplicate unlink"); retval = -1; } else { retval = 0; } } else if (retval < 0) { perror("unlink"); } free(p); return retval; } int main(int argc, char *argv[]) { int ret; struct crut_operations fd_read_test_ops = { test_scope:CR_SCOPE_PROC, test_name:"fd_read", test_description:"Test that fd's open for read work.", test_setup:fd_read_setup, test_precheckpoint:fd_generic_precheckpoint, test_continue:fd_generic_continue, test_restart:fd_read_restart, test_teardown:fd_generic_teardown, }; struct crut_operations fd_write_test_ops = { test_scope:CR_SCOPE_PROC, test_name:"fd_write", test_description:"Test fd's open for writing.", test_setup:fd_write_setup, test_precheckpoint:fd_generic_precheckpoint, test_continue:fd_generic_continue, test_restart:fd_write_restart, test_teardown:fd_generic_teardown, }; struct crut_operations fd_rdwr_test_ops = { test_scope:CR_SCOPE_PROC, test_name:"fd_rdwr", test_description:"Test fd's open O_RDWR.", test_setup:fd_rdwr_setup, test_precheckpoint:fd_generic_precheckpoint, test_continue:fd_generic_continue, test_restart:fd_rdwr_restart, test_teardown:fd_generic_teardown, }; struct crut_operations fd_flags_test_ops = { test_scope:CR_SCOPE_PROC, test_name:"fd_flags", test_description:"Test flags on open files", test_setup:fd_flags_setup, test_precheckpoint:fd_flags_precheckpoint_continue, test_continue:fd_flags_precheckpoint_continue, test_restart:fd_flags_restart, test_teardown:fd_flags_teardown, }; struct crut_operations fd_read_unlinked_test_ops = { test_scope:CR_SCOPE_PROC, test_name:"fd_read_unlinked", test_description:"Test that fd's open for read work.", test_setup:fd_read_unlinked_setup, test_precheckpoint:fd_generic_precheckpoint, test_continue:fd_generic_continue, test_restart:fd_read_restart, test_teardown:fd_generic_teardown, }; struct crut_operations fd_write_unlinked_test_ops = { test_scope:CR_SCOPE_PROC, test_name:"fd_write_unlinked", test_description:"Test fd's open for writing.", test_setup:fd_write_unlinked_setup, test_precheckpoint:fd_generic_precheckpoint, test_continue:fd_generic_continue, test_restart:fd_write_restart, test_teardown:fd_generic_teardown, }; struct crut_operations fd_rdwr_unlinked_test_ops = { test_scope:CR_SCOPE_PROC, test_name:"fd_rdwr_unlinked", test_description:"Test fd's open O_RDWR.", test_setup:fd_rdwr_unlinked_setup, test_precheckpoint:fd_generic_precheckpoint, test_continue:fd_generic_continue, test_restart:fd_rdwr_restart, test_teardown:fd_generic_teardown, }; struct crut_operations fd_flags_unlinked_test_ops = { test_scope:CR_SCOPE_PROC, test_name:"fd_flags_unlinked", test_description:"Test flags on open files", test_setup:fd_flags_unlinked_setup, test_precheckpoint:fd_flags_precheckpoint_continue, test_continue:fd_flags_precheckpoint_continue, test_restart:fd_flags_restart, test_teardown:fd_flags_teardown, }; /* add the basic tests */ crut_add_test(&fd_read_test_ops); crut_add_test(&fd_write_test_ops); crut_add_test(&fd_rdwr_test_ops); crut_add_test(&fd_flags_test_ops); /* add the unlinked tests */ crut_add_test(&fd_read_unlinked_test_ops); crut_add_test(&fd_write_unlinked_test_ops); crut_add_test(&fd_rdwr_unlinked_test_ops); crut_add_test(&fd_flags_unlinked_test_ops); ret = crut_main(argc, argv); return ret; }