/*****************************************************************************\ * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. * Copyright (C) 2007 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Brian Behlendorf . * UCRL-CODE-235197 * * This file is part of the SPL, Solaris Porting Layer. * For details, see . * * The SPL 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. * * The SPL 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 the SPL. If not, see . ***************************************************************************** * Solaris Porting LAyer Tests (SPLAT) Vnode Tests. \*****************************************************************************/ #include "splat-internal.h" #include #define SPLAT_VNODE_NAME "vnode" #define SPLAT_VNODE_DESC "Kernel Vnode Tests" #define SPLAT_VNODE_TEST1_ID 0x0901 #define SPLAT_VNODE_TEST1_NAME "vn_open" #define SPLAT_VNODE_TEST1_DESC "Vn_open Test" #define SPLAT_VNODE_TEST2_ID 0x0902 #define SPLAT_VNODE_TEST2_NAME "vn_openat" #define SPLAT_VNODE_TEST2_DESC "Vn_openat Test" #define SPLAT_VNODE_TEST3_ID 0x0903 #define SPLAT_VNODE_TEST3_NAME "vn_rdwr" #define SPLAT_VNODE_TEST3_DESC "Vn_rdwrt Test" #define SPLAT_VNODE_TEST4_ID 0x0904 #define SPLAT_VNODE_TEST4_NAME "vn_rename" #define SPLAT_VNODE_TEST4_DESC "Vn_rename Test" #define SPLAT_VNODE_TEST5_ID 0x0905 #define SPLAT_VNODE_TEST5_NAME "vn_getattr" #define SPLAT_VNODE_TEST5_DESC "Vn_getattr Test" #define SPLAT_VNODE_TEST6_ID 0x0906 #define SPLAT_VNODE_TEST6_NAME "vn_sync" #define SPLAT_VNODE_TEST6_DESC "Vn_sync Test" #define SPLAT_VNODE_TEST7_ID 0x0907 #define SPLAT_VNODE_TEST7_NAME "vn_getf" #define SPLAT_VNODE_TEST7_DESC "vn_getf/vn_releasef Test" #define SPLAT_VNODE_TEST_FILE "/etc/fstab" #define SPLAT_VNODE_TEST_FILE_AT "etc/fstab" #define SPLAT_VNODE_TEST_FILE_RW "/tmp/spl.vnode.tmp" #define SPLAT_VNODE_TEST_FILE_RW1 "/tmp/spl.vnode.tmp.1" #define SPLAT_VNODE_TEST_FILE_RW2 "/tmp/spl.vnode.tmp.2" static int splat_vnode_user_cmd(struct file *file, void *arg, char *name, char *cmd) { char sh_path[] = "/bin/sh"; char *argv[] = { sh_path, "-c", cmd, NULL }; char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; int rc; rc = call_usermodehelper(sh_path, argv, envp, 1); if (rc) { splat_vprint(file, name, "Failed command: %s %s %s (%d)\n", argv[0], argv[1], cmd, rc); return -EPERM; } return 0; } static int splat_vnode_unlink_all(struct file *file, void *arg, char *name) { char *cmds[] = { "rm -f " SPLAT_VNODE_TEST_FILE_RW, "rm -f " SPLAT_VNODE_TEST_FILE_RW1, "rm -f " SPLAT_VNODE_TEST_FILE_RW2, NULL }; int i = 0, rc = 0; while (cmds[i] != NULL) { if ((rc = splat_vnode_user_cmd(file, arg, name, cmds[i]))) return rc; i++; } return rc; } static int splat_vnode_test1(struct file *file, void *arg) { vnode_t *vp; int rc; if ((rc = vn_open(SPLAT_VNODE_TEST_FILE, UIO_SYSSPACE, FREAD, 0644, &vp, 0, 0))) { splat_vprint(file, SPLAT_VNODE_TEST1_NAME, "Failed to vn_open test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE, rc); return -rc; } rc = VOP_CLOSE(vp, 0, 0, 0, 0, 0); if (rc) { splat_vprint(file, SPLAT_VNODE_TEST1_NAME, "Failed to vn_close test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE, rc); return -rc; } splat_vprint(file, SPLAT_VNODE_TEST1_NAME, "Successfully vn_open'ed " "and vn_closed test file: %s\n", SPLAT_VNODE_TEST_FILE); return -rc; } /* splat_vnode_test1() */ static int splat_vnode_test2(struct file *file, void *arg) { vnode_t *vp; int rc; if ((rc = vn_openat(SPLAT_VNODE_TEST_FILE_AT, UIO_SYSSPACE, FREAD, 0644, &vp, 0, 0, rootdir, 0))) { splat_vprint(file, SPLAT_VNODE_TEST2_NAME, "Failed to vn_openat test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE, rc); return -rc; } rc = VOP_CLOSE(vp, 0, 0, 0, 0, 0); if (rc) { splat_vprint(file, SPLAT_VNODE_TEST2_NAME, "Failed to vn_close test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE, rc); return -rc; } splat_vprint(file, SPLAT_VNODE_TEST2_NAME, "Successfully vn_openat'ed " "and vn_closed test file: %s\n", SPLAT_VNODE_TEST_FILE); return -rc; } /* splat_vnode_test2() */ static int splat_vnode_test3(struct file *file, void *arg) { vnode_t *vp; char buf1[32] = "SPL VNode Interface Test File\n"; char buf2[32] = ""; int rc; if ((rc = splat_vnode_unlink_all(file, arg, SPLAT_VNODE_TEST3_NAME))) return rc; if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE, FWRITE | FREAD | FCREAT | FEXCL, 0644, &vp, 0, 0))) { splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Failed to vn_open test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW, rc); return -rc; } rc = vn_rdwr(UIO_WRITE, vp, buf1, strlen(buf1), 0, UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); if (rc) { splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Failed vn_rdwr write of test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW, rc); goto out; } rc = vn_rdwr(UIO_READ, vp, buf2, strlen(buf1), 0, UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); if (rc) { splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Failed vn_rdwr read of test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW, rc); goto out; } if (strncmp(buf1, buf2, strlen(buf1))) { rc = EINVAL; splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Failed strncmp data written does not match " "data read\nWrote: %sRead: %s\n", buf1, buf2); goto out; } rc = 0; splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Wrote: %s", buf1); splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Read: %s", buf2); splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Successfully wrote and " "read expected data pattern to test file: %s\n", SPLAT_VNODE_TEST_FILE_RW); out: VOP_CLOSE(vp, 0, 0, 0, 0, 0); vn_remove(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE, RMFILE); return -rc; } /* splat_vnode_test3() */ static int splat_vnode_test4(struct file *file, void *arg) { vnode_t *vp; char buf1[32] = "SPL VNode Interface Test File\n"; char buf2[32] = ""; int rc; if ((rc = splat_vnode_unlink_all(file, arg, SPLAT_VNODE_TEST4_NAME))) return rc; if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW1, UIO_SYSSPACE, FWRITE | FREAD | FCREAT | FEXCL, 0644, &vp, 0, 0))) { splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Failed to vn_open test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW1, rc); goto out; } rc = vn_rdwr(UIO_WRITE, vp, buf1, strlen(buf1), 0, UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); if (rc) { splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Failed vn_rdwr write of test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW1, rc); goto out2; } VOP_CLOSE(vp, 0, 0, 0, 0, 0); rc = vn_rename(SPLAT_VNODE_TEST_FILE_RW1,SPLAT_VNODE_TEST_FILE_RW2,0); if (rc) { splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Failed vn_rename " "%s -> %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW1, SPLAT_VNODE_TEST_FILE_RW2, rc); goto out; } if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW2, UIO_SYSSPACE, FREAD | FEXCL, 0644, &vp, 0, 0))) { splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Failed to vn_open test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW2, rc); goto out; } rc = vn_rdwr(UIO_READ, vp, buf2, strlen(buf1), 0, UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); if (rc) { splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Failed vn_rdwr read of test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW2, rc); goto out2; } if (strncmp(buf1, buf2, strlen(buf1))) { rc = EINVAL; splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Failed strncmp data written does not match " "data read\nWrote: %sRead: %s\n", buf1, buf2); goto out2; } rc = 0; splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Wrote to %s: %s", SPLAT_VNODE_TEST_FILE_RW1, buf1); splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Read from %s: %s", SPLAT_VNODE_TEST_FILE_RW2, buf2); splat_vprint(file, SPLAT_VNODE_TEST4_NAME, "Successfully renamed " "test file %s -> %s and verified data pattern\n", SPLAT_VNODE_TEST_FILE_RW1, SPLAT_VNODE_TEST_FILE_RW2); out2: VOP_CLOSE(vp, 0, 0, 0, 0, 0); out: vn_remove(SPLAT_VNODE_TEST_FILE_RW1, UIO_SYSSPACE, RMFILE); vn_remove(SPLAT_VNODE_TEST_FILE_RW2, UIO_SYSSPACE, RMFILE); return -rc; } /* splat_vnode_test4() */ static int splat_vnode_test5(struct file *file, void *arg) { vnode_t *vp; vattr_t vap; int rc; if ((rc = vn_open(SPLAT_VNODE_TEST_FILE, UIO_SYSSPACE, FREAD, 0644, &vp, 0, 0))) { splat_vprint(file, SPLAT_VNODE_TEST5_NAME, "Failed to vn_open test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE, rc); return -rc; } rc = VOP_GETATTR(vp, &vap, 0, 0, NULL); if (rc) { splat_vprint(file, SPLAT_VNODE_TEST5_NAME, "Failed to vn_getattr test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE, rc); goto out; } if (vap.va_type != VREG) { rc = EINVAL; splat_vprint(file, SPLAT_VNODE_TEST5_NAME, "Failed expected regular file type " "(%d != VREG): %s (%d)\n", vap.va_type, SPLAT_VNODE_TEST_FILE, rc); goto out; } splat_vprint(file, SPLAT_VNODE_TEST1_NAME, "Successfully " "vn_getattr'ed test file: %s\n", SPLAT_VNODE_TEST_FILE); out: VOP_CLOSE(vp, 0, 0, 0, 0, 0); return -rc; } /* splat_vnode_test5() */ static int splat_vnode_test6(struct file *file, void *arg) { vnode_t *vp; char buf[32] = "SPL VNode Interface Test File\n"; int rc; if ((rc = splat_vnode_unlink_all(file, arg, SPLAT_VNODE_TEST6_NAME))) return rc; if ((rc = vn_open(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE, FWRITE | FCREAT | FEXCL, 0644, &vp, 0, 0))) { splat_vprint(file, SPLAT_VNODE_TEST6_NAME, "Failed to vn_open test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW, rc); return -rc; } rc = vn_rdwr(UIO_WRITE, vp, buf, strlen(buf), 0, UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); if (rc) { splat_vprint(file, SPLAT_VNODE_TEST6_NAME, "Failed vn_rdwr write of test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW, rc); goto out; } rc = vn_fsync(vp, 0, 0, 0); if (rc) { splat_vprint(file, SPLAT_VNODE_TEST6_NAME, "Failed vn_fsync of test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW, rc); goto out; } rc = 0; splat_vprint(file, SPLAT_VNODE_TEST6_NAME, "Successfully " "fsync'ed test file %s\n", SPLAT_VNODE_TEST_FILE_RW); out: VOP_CLOSE(vp, 0, 0, 0, 0, 0); vn_remove(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE, RMFILE); return -rc; } /* splat_vnode_test6() */ /* Basically a slightly modified version of sys_close() */ static int fd_uninstall(int fd) { struct file *fp; struct files_struct *files = current->files; #ifdef HAVE_FILES_FDTABLE struct fdtable *fdt; spin_lock(&files->file_lock); fdt = files_fdtable(files); if (fd >= fdt->max_fds) goto out_unlock; fp = fdt->fd[fd]; if (!fp) goto out_unlock; rcu_assign_pointer(fdt->fd[fd], NULL); __clear_close_on_exec(fd, fdt); #else spin_lock(&files->file_lock); if (fd >= files->max_fds) goto out_unlock; fp = files->fd[fd]; if (!fp) goto out_unlock; files->fd[fd] = NULL; FD_CLR(fd, files->close_on_exec); #endif /* Dropping the lock here exposes a minor race but it allows me * to use the existing kernel interfaces for this, and for a test * case I think that's reasonable. */ spin_unlock(&files->file_lock); put_unused_fd(fd); return 0; out_unlock: spin_unlock(&files->file_lock); return -EBADF; } /* fd_uninstall() */ static int splat_vnode_test7(struct file *file, void *arg) { char buf1[32] = "SPL VNode Interface Test File\n"; char buf2[32] = ""; struct file *lfp; file_t *fp; int rc, fd; if ((rc = splat_vnode_unlink_all(file, arg, SPLAT_VNODE_TEST7_NAME))) return rc; /* Prep work needed to test getf/releasef */ fd = get_unused_fd(); if (fd < 0) { splat_vprint(file, SPLAT_VNODE_TEST7_NAME, "Failed to get unused fd (%d)\n", fd); return fd; } lfp = filp_open(SPLAT_VNODE_TEST_FILE_RW, O_RDWR|O_CREAT|O_EXCL, 0644); if (IS_ERR(lfp)) { put_unused_fd(fd); rc = PTR_ERR(lfp); splat_vprint(file, SPLAT_VNODE_TEST7_NAME, "Failed to filp_open: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW, rc); return rc; } /* Pair up the new fd and lfp in the current context, this allows * getf to lookup the file struct simply by the known open fd */ fd_install(fd, lfp); /* Actual getf()/releasef() test */ fp = vn_getf(fd); if (fp == NULL) { rc = EINVAL; splat_vprint(file, SPLAT_VNODE_TEST7_NAME, "Failed to getf fd %d: (%d)\n", fd, rc); goto out; } rc = vn_rdwr(UIO_WRITE, fp->f_vnode, buf1, strlen(buf1), 0, UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); if (rc) { splat_vprint(file, SPLAT_VNODE_TEST7_NAME, "Failed vn_rdwr write of test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW, rc); goto out; } rc = vn_rdwr(UIO_READ, fp->f_vnode, buf2, strlen(buf1), 0, UIO_SYSSPACE, 0, RLIM64_INFINITY, 0, NULL); if (rc) { splat_vprint(file, SPLAT_VNODE_TEST7_NAME, "Failed vn_rdwr read of test file: %s (%d)\n", SPLAT_VNODE_TEST_FILE_RW, rc); goto out; } if (strncmp(buf1, buf2, strlen(buf1))) { rc = EINVAL; splat_vprint(file, SPLAT_VNODE_TEST7_NAME, "Failed strncmp data written does not match " "data read\nWrote: %sRead: %s\n", buf1, buf2); goto out; } rc = 0; splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Wrote: %s", buf1); splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Read: %s", buf2); splat_vprint(file, SPLAT_VNODE_TEST3_NAME, "Successfully wrote and " "read expected data pattern to test file: %s\n", SPLAT_VNODE_TEST_FILE_RW); out: vn_releasef(fd); fd_uninstall(fd); filp_close(lfp, 0); vn_remove(SPLAT_VNODE_TEST_FILE_RW, UIO_SYSSPACE, RMFILE); return -rc; } /* splat_vnode_test7() */ splat_subsystem_t * splat_vnode_init(void) { splat_subsystem_t *sub; sub = kmalloc(sizeof(*sub), GFP_KERNEL); if (sub == NULL) return NULL; memset(sub, 0, sizeof(*sub)); strncpy(sub->desc.name, SPLAT_VNODE_NAME, SPLAT_NAME_SIZE); strncpy(sub->desc.desc, SPLAT_VNODE_DESC, SPLAT_DESC_SIZE); INIT_LIST_HEAD(&sub->subsystem_list); INIT_LIST_HEAD(&sub->test_list); spin_lock_init(&sub->test_lock); sub->desc.id = SPLAT_SUBSYSTEM_VNODE; SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST1_NAME, SPLAT_VNODE_TEST1_DESC, SPLAT_VNODE_TEST1_ID, splat_vnode_test1); SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST2_NAME, SPLAT_VNODE_TEST2_DESC, SPLAT_VNODE_TEST2_ID, splat_vnode_test2); SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST3_NAME, SPLAT_VNODE_TEST3_DESC, SPLAT_VNODE_TEST3_ID, splat_vnode_test3); SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST4_NAME, SPLAT_VNODE_TEST4_DESC, SPLAT_VNODE_TEST4_ID, splat_vnode_test4); SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST5_NAME, SPLAT_VNODE_TEST5_DESC, SPLAT_VNODE_TEST5_ID, splat_vnode_test5); SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST6_NAME, SPLAT_VNODE_TEST6_DESC, SPLAT_VNODE_TEST6_ID, splat_vnode_test6); SPLAT_TEST_INIT(sub, SPLAT_VNODE_TEST7_NAME, SPLAT_VNODE_TEST7_DESC, SPLAT_VNODE_TEST7_ID, splat_vnode_test7); return sub; } /* splat_vnode_init() */ void splat_vnode_fini(splat_subsystem_t *sub) { ASSERT(sub); SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST7_ID); SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST6_ID); SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST5_ID); SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST4_ID); SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST3_ID); SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST2_ID); SPLAT_TEST_FINI(sub, SPLAT_VNODE_TEST1_ID); kfree(sub); } /* splat_vnode_fini() */ int splat_vnode_id(void) { return SPLAT_SUBSYSTEM_VNODE; } /* splat_vnode_id() */