From: fsgqa Date: Mon, 15 Sep 2003 05:11:31 +0000 (+0000) Subject: xfstests update - Ethans new inode flags test mainly X-Git-Tag: v1.1.0~920 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=f59c77fbcb5bedf00865d5cf225119ea650d7d7c;p=xfstests-dev.git xfstests update - Ethans new inode flags test mainly --- diff --git a/079 b/079 new file mode 100755 index 00000000..bb55f0c2 --- /dev/null +++ b/079 @@ -0,0 +1,71 @@ +#! /bin/sh +# XFS QA Test No. 079 +# +# Run the t_immutable test program for immutable/append-only files. +# +#----------------------------------------------------------------------- +# Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# Further, this software is distributed without any warranty that it is +# free of the rightful claim of any third person regarding infringement +# or the like. Any license provided herein, whether implied or +# otherwise, applies only to this software file. Patent licenses, if +# any, provided herein do not apply to combinations of this program with +# other software, or any other product whatsoever. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write the Free Software Foundation, Inc., 59 +# Temple Place - Suite 330, Boston MA 02111-1307, USA. +# +# Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, +# Mountain View, CA 94043, or: +# +# http://www.sgi.com +# +# For further information regarding this notice, see: +# +# http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ +#----------------------------------------------------------------------- +# +# creator +owner=nathans@sgi.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +timmutable=$here/src/t_immutable +tmp=/tmp/$$ +status=1 # failure is the default! +trap "_cleanup; exit \$status" 0 1 2 3 15 + +_cleanup() +{ + echo "*** cleaning up" + $timmutable -r $SCRATCH_MNT/$seq + umount $SCRATCH_MNT +} + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_require_scratch +[ -x $timmutable ] || _notrun "t_immutable was not built for this platform" + +# real QA test starts here +_scratch_mkfs_xfs 2>&1 >/dev/null || _fail "mkfs failed" +_scratch_mount || _fail "mount failed" + +echo "*** starting up" +$timmutable -c $SCRATCH_MNT/$seq +status=$? +exit diff --git a/079.out b/079.out new file mode 100644 index 00000000..b3daaa6a --- /dev/null +++ b/079.out @@ -0,0 +1,7 @@ +QA output created by 079 +*** starting up +testing immutable...PASS. +testing append-only...PASS. +testing immutable as non-root...PASS. +testing append-only as non-root...PASS. +*** cleaning up diff --git a/group b/group index b6d33f57..e1e1b2be 100644 --- a/group +++ b/group @@ -139,3 +139,4 @@ ioctl nathans@sgi.com 076 metadata rw 077 acl attr auto 078 growfs auto +079 acl attr ioctl metadata auto diff --git a/src/Makefile b/src/Makefile index daa1f736..5bbb6c1e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -41,6 +41,9 @@ TARGETS = alloc acl_get bstat devzero dirstress fault feature \ ifeq ($(ENABLE_DBM), yes) TARGETS += dbtest endif +ifeq ($(PKG_PLATFORM),linux) +TARGETS += t_immutable +endif CFILES = $(TARGETS:=.c) LDIRT = $(TARGETS) @@ -65,6 +68,9 @@ nametest: nametest.o $(LIBTEST) bstat: bstat.o $(LIBHANDLE) $(LINKTEST) $(LIBHANDLE) $(LDLIBS) +t_immutable: t_immutable.o $(LIBHANDLE) $(LIBACL) + $(LINKTEST) $(LIBACL) $(LIBHANDLE) $(LDLIBS) + loggen: loggen.o $(LINKTEST) $(LDLIBS) diff --git a/src/t_immutable.c b/src/t_immutable.c new file mode 100644 index 00000000..854986ed --- /dev/null +++ b/src/t_immutable.c @@ -0,0 +1,2341 @@ +/* compile with: gcc -g -O0 -Wall -I/usr/include/xfs -o t_immutable t_immutable.c -lhandle -lacl -lattr */ + +/* + * t_immutable.c - hideous test suite for immutable/append-only flags. + * + * Copyright (C) 2003 Ethan Benson + * + * 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. + */ + +#define TEST_UTIME + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define XFS_XFLAG_IMMUTABLE 0x00000008 +#define XFS_XFLAG_APPEND 0x00000010 +#define EXT2_IMMUTABLE_FL 0x00000010 +#define EXT2_APPEND_FL 0x00000020 + +extern const char *__progname; + +static int fsetflag(const char *path, int fd, int on, int immutable) +{ + int e2flags = 0; + struct fsxattr attr; + struct statfs stfs; + int xfsfl; + int e2fl; + + if (immutable) { + xfsfl = XFS_XFLAG_IMMUTABLE; + e2fl = EXT2_IMMUTABLE_FL; + } else { + xfsfl = XFS_XFLAG_APPEND; + e2fl = EXT2_APPEND_FL; + } + + if (fstatfs(fd, &stfs) != 0) + return 1; + + if (stfs.f_type == XFS_SUPER_MAGIC) { + if (xfsctl(path, fd, XFS_IOC_FSGETXATTR, &attr) < 0) { + close(fd); + return 1; + } + if (on) + attr.fsx_xflags |= xfsfl; + else + attr.fsx_xflags &= ~xfsfl; + if (xfsctl(path, fd, XFS_IOC_FSSETXATTR, &attr) < 0) { + close(fd); + return 1; + } + } else if (stfs.f_type == EXT2_SUPER_MAGIC) { + if (on) + e2flags |= e2fl; + else + e2flags &= ~e2fl; + if (ioctl(fd, EXT2_IOC_SETFLAGS, &e2flags) < 0) { + close(fd); + return 1; + } + } else { + errno = EOPNOTSUPP; + close(fd); + return 1; + } + close(fd); + return 0; +} + +static int add_acl(const char *path, const char *acl_text) +{ + acl_t acl; + int s_errno; + int fd; + + if ((acl = acl_from_text(acl_text)) == NULL) + return 1; + if ((fd = open(path, O_RDONLY)) == -1) + return 1; + if (acl_set_fd(fd, acl)) + if (errno != EOPNOTSUPP) + return 1; + s_errno = errno; + acl_free(acl); + close(fd); + errno = s_errno; + return 0; +} + +static int fadd_acl(int fd, const char *acl_text) +{ + acl_t acl; + int s_errno; + + if ((acl = acl_from_text(acl_text)) == NULL) + perror("acl_from_text"); + if (acl_set_fd(fd, acl)) + if (errno != EOPNOTSUPP) + return 1; + s_errno = errno; + acl_free(acl); + errno = s_errno; + return 0; +} + +static int del_acl(const char *path) +{ + int fd; + int s_errno; + acl_t acl; + static const char *acl_text = "u::rw-,g::rw-,o::rw-"; + + if ((acl = acl_from_text(acl_text)) == NULL) + return 1; + if ((fd = open(path, O_RDONLY)) == -1) + return 1; + if (acl_set_fd(fd, acl)) + if (errno != EOPNOTSUPP) + return 1; + s_errno = errno; + acl_free(acl); + close(fd); + errno = s_errno; + return 0; +} + +static int test_immutable(const char *dir) +{ + int fd; + char *buf; + char *path; + char *linkpath; + int fail = 0; + struct utimbuf tbuf; + struct stat st; + struct statfs stfs; + static const char *scribble = "scribbled by tester\n"; + static const char *acl_text = "u::rwx,g::rwx,o::rwx,u:daemon:rwx,m::rwx"; + + tbuf.actime = 0; + tbuf.modtime = 0; + + if (statfs(dir, &stfs) == -1) { + perror("statfs failed"); + return 1; + } + + asprintf(&path, "%s/immutable.f", dir); + errno = 0; + if ((fd = open(path, O_RDWR)) != -1) { + fprintf(stderr, "open(%s, O_RDWR) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EACCES) { + fprintf(stderr, "open(%s, O_RDWR) did not set errno == EACCES\n", path); + fail++; + } + + errno = 0; + if ((fd = open(path, O_WRONLY)) != -1) { + fprintf(stderr, "open(%s, O_WRONLY) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EACCES) { + fprintf(stderr, "open(%s, O_WRONLY) did not set errno == EACCES\n", path); + fail++; + } + + errno = 0; + if ((fd = open(path, O_RDWR|O_TRUNC)) != -1) { + fprintf(stderr, "open(%s, O_RDWR|O_TRUNC) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EACCES) { + fprintf(stderr, "open(%s, O_RDWR|O_TRUNC) did not set errno == EACCES\n", path); + fail++; + } + + errno = 0; + if ((fd = open(path, O_WRONLY|O_TRUNC)) != -1) { + fprintf(stderr, "open(%s, O_WRONLY|O_TRUNC) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EACCES) { + fprintf(stderr, "open(%s, O_WRONLY|O_TRUNC did not set errno == EACCES\n", path); + fail++; + } + + errno = 0; + if ((fd = open(path, O_RDWR|O_APPEND)) != -1) { + fprintf(stderr, "open(%s, O_RDWR|O_APPEND) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EACCES) { + fprintf(stderr, "open(%s, O_RDWR|O_APPEND) did not set errno == EACCES\n", path); + fail++; + } + + errno = 0; + if ((fd = open(path, O_WRONLY|O_APPEND)) != -1) { + fprintf(stderr, "open(%s, O_WRONLY|O_APPEND) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EACCES) { + fprintf(stderr, "open(%s, O_WRONLY|O_APPEND) did not set errno == EACCES\n", path); + fail++; + } + + errno = 0; + if ((fd = open(path, O_RDWR|O_APPEND|O_TRUNC)) != -1) { + fprintf(stderr, "open(%s, O_RDWR|O_APPEND|O_TRUNC) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EACCES) { + fprintf(stderr, "open(%s, O_RDWR|O_APPEND|O_TRUNC) did not set errno == EACCES\n", path); + fail++; + } + + errno = 0; + if ((fd = open(path, O_WRONLY|O_APPEND|O_TRUNC)) != -1) { + fprintf(stderr, "open(%s, O_WRONLY|O_APPEND|O_TRUNC) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EACCES) { + fprintf(stderr, "open(%s, O_WRONLY|O_APPEND|O_TRUNC) did not set errno == EACCES\n", path); + fail++; + } + + if (stfs.f_type == XFS_SUPER_MAGIC && !getuid()) { + jdm_fshandle_t *fshandle; + xfs_bstat_t bstat; + xfs_fsop_bulkreq_t bulkreq; + xfs_ino_t ino; + char *dirpath; + + dirpath = strdup(path); /* dirname obnoxiously modifies its arg */ + if ((fshandle = jdm_getfshandle(dirname(dirpath))) == NULL) { + perror("jdm_getfshandle"); + return 1; + } + free(dirpath); + + if (stat(path, &st) != 0) { + perror("stat"); + return 1; + } + + ino = st.st_ino; + + bulkreq.lastip = &ino; + bulkreq.icount = 1; + bulkreq.ubuffer = &bstat; + bulkreq.ocount = NULL; + + if ((fd = open(path, O_RDONLY)) == -1) { + perror("open"); + return 1; + } + + if (ioctl(fd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq) == -1) { + perror("ioctl(fd, XFS_IOC_FSBULKSTAT_SINGLE"); + close(fd); + return 1; + } + close(fd); + + errno = 0; + if ((fd = jdm_open(fshandle, &bstat, O_RDWR)) != -1) { + fprintf(stderr, "jdm_open(%s, O_RDWR) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EACCES) { + perror("jdm_open"); + fprintf(stderr, "jdm_open(%s, O_RDWR) did not set errno == EACCES\n", path); + fail++; + } + + errno = 0; + if ((fd = jdm_open(fshandle, &bstat, O_WRONLY)) != -1) { + fprintf(stderr, "jdm_open(%s, O_WRONLY) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EACCES) { + fprintf(stderr, "jdm_open(%s, O_WRONLY) did not set errno == EACCES\n", path); + fail++; + } + + errno = 0; + if ((fd = jdm_open(fshandle, &bstat, O_RDWR|O_TRUNC)) != -1) { + fprintf(stderr, "jdm_open(%s, O_RDWR|O_TRUNC) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EACCES) { + fprintf(stderr, "jdm_open(%s, O_RDWR|O_TRUNC) did not set errno == EACCES\n", path); + fail++; + } + + errno = 0; + if ((fd = jdm_open(fshandle, &bstat, O_WRONLY|O_TRUNC)) != -1) { + fprintf(stderr, "jdm_open(%s, O_WRONLY|O_TRUNC) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EACCES) { + fprintf(stderr, "jdm_open(%s, O_WRONLY|O_TRUNC did not set errno == EACCES\n", path); + fail++; + } + + errno = 0; + if ((fd = jdm_open(fshandle, &bstat, O_RDWR|O_APPEND)) != -1) { + fprintf(stderr, "jdm_open(%s, O_RDWR|O_APPEND) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EACCES) { + fprintf(stderr, "jdm_open(%s, O_RDWR|O_APPEND) did not set errno == EACCES\n", path); + fail++; + } + + errno = 0; + if ((fd = jdm_open(fshandle, &bstat, O_WRONLY|O_APPEND)) != -1) { + fprintf(stderr, "jdm_open(%s, O_WRONLY|O_APPEND) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EACCES) { + fprintf(stderr, "jdm_open(%s, O_WRONLY|O_APPEND) did not set errno == EACCES\n", path); + fail++; + } + + errno = 0; + if ((fd = jdm_open(fshandle, &bstat, O_RDWR|O_APPEND|O_TRUNC)) != -1) { + fprintf(stderr, "jdm_open(%s, O_RDWR|O_APPEND|O_TRUNC) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EACCES) { + fprintf(stderr, "jdm_open(%s, O_RDWR|O_APPEND|O_TRUNC) did not set errno == EACCES\n", path); + fail++; + } + + errno = 0; + if ((fd = jdm_open(fshandle, &bstat, O_WRONLY|O_APPEND|O_TRUNC)) != -1) { + fprintf(stderr, "jdm_open(%s, O_WRONLY|O_APPEND|O_TRUNC) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EACCES) { + fprintf(stderr, "jdm_open(%s, O_WRONLY|O_APPEND|O_TRUNC) did not set errno == EACCES\n", path); + fail++; + } + } + + errno = 0; + if (truncate(path, 0) != -1) { + fprintf(stderr, "truncate(%s, 0) did not fail\n", path); + fail++; + } else if (errno != EACCES) { + fprintf(stderr, "truncate(%s, 0) did not set errno == EACCES\n", path); + fail++; + } + +#ifdef TEST_UTIME + errno = 0; + if (utime(path, &tbuf) != -1) { + fprintf(stderr, "utime(%s, ) did not fail\n", path); + fail++; + } else if (errno != EPERM) { + fprintf(stderr, "utime(%s, ) did not set errno == EPERM\n", path); + fail++; + } + + errno = 0; + if (utime(path, NULL) != -1) { + fprintf(stderr, "utime(%s, NULL) did not fail\n", path); + fail++; + } else if (errno != EACCES) { + fprintf(stderr, "utime(%s, NULL) did not set errno == EACCES\n", path); + fail++; + } +#endif /* TEST_UTIME */ + + asprintf(&linkpath, "%s/immutable.f.hardlink", dir); + errno = 0; + if (link(path, linkpath) != -1) { + fprintf(stderr, "link(%s, %s) did not fail\n", path, linkpath); + fail++; + unlink(linkpath); + } else if (errno != EPERM) { + fprintf(stderr, "link(%s, %s) did not set errno == EPERM\n", path, linkpath); + fail++; + } + free(linkpath); + + if (!getuid()) { /* these would fail if not root anyway */ + errno = 0; + if (chmod(path, 7777) != -1) { + fprintf(stderr, "chmod(%s, 7777) did not fail\n", path); + fail++; + chmod(path, 0666); + } else if (errno != EPERM) { + fprintf(stderr, "chmod(%s, 7777) did not set errno == EPERM\n", path); + fail++; + } + + errno = 0; + if (chown(path, 1, 1) != -1) { + fprintf(stderr, "chown(%s, 1, 1) did not fail\n", path); + fail++; + chown(path, 0, 0); + } else if (errno != EPERM) { + fprintf(stderr, "chown(%s, 1, 1) did not set errno to EPERM\n", path); + fail++; + } + + errno = 0; + if (del_acl(path) != 1) { + if (errno != EOPNOTSUPP) { + fprintf(stderr, "del_acl(%s) did not fail\n", path); + fail++; + } + } else if (errno != EPERM) { + fprintf(stderr, "del_acl(%s) did not set errno == EPERM\n", path); + fail++; + } + errno = 0; + if (add_acl(path, acl_text) != 1) { + if (errno != EOPNOTSUPP) { + fprintf(stderr, "add_acl(%s) did not fail\n", path); + fail++; + } + } else if (errno != EPERM) { + fprintf(stderr, "add_acl(%s) did not set errno == EPERM\n", path); + fail++; + } + + if (setxattr(path, "trusted.test", "scribble", strlen("scribble"), XATTR_REPLACE) != -1) { + fprintf(stderr, "setxattr(%s, trusted.test, scribble, 8, XATTR_REPLACE) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, + "setxattr(%s, trusted.test, scribble, 8, XATTR_REPLACE) did not set errno == EPERM\n", path); + fail++; + } + if (setxattr(path, "trusted.scribble", "scribble", strlen("scribble"), XATTR_CREATE) != -1) { + fprintf(stderr, "setxattr(%s, trusted.scribble, scribble, 8, XATTR_CREATE) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, + "setxattr(%s, trusted.scribble, scribble, 8, XATTR_CREATE) did not set errno == EPERM\n", path); + fail++; + } + + if (removexattr(path, "trusted.test") != -1) { + fprintf(stderr, "removexattr(%s, trusted.test) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, + "removexattr(%s, trusted.test) did not set errno == EPERM\n", path); + fail++; + } + } + + if (setxattr(path, "user.test", "scribble", strlen("scribble"), XATTR_REPLACE) != -1) { + fprintf(stderr, "setxattr(%s, user.test, scribble, 8, XATTR_REPLACE) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, "setxattr(%s, user.test, scribble, 8, XATTR_REPLACE) did not set errno == EPERM\n", path); + fail++; + } + if (setxattr(path, "user.scribble", "scribble", strlen("scribble"), XATTR_CREATE) != -1) { + fprintf(stderr, "setxattr(%s, user.scribble, scribble, 8, XATTR_CREATE) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, "setxattr(%s, user.scribble, scribble, 8, XATTR_CREATE) did not set errno == EPERM\n", path); + fail++; + } + if (removexattr(path, "user.test") != -1) { + fprintf(stderr, "removexattr(%s, user.test) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, + "removexattr(%s, user.test) did not set errno == EPERM\n", path); + fail++; + } + + asprintf(&linkpath, "%s/immutable.f.newname", dir); + errno = 0; + if (rename(path, linkpath) != -1) { + fprintf(stderr, "rename(%s, %s) did not fail\n", path, linkpath); + fail++; + rename(linkpath, path); + } else if (errno != EPERM) { + fprintf(stderr, "rename(%s, %s) did not set errno == EPERM\n", path, linkpath); + fail++; + } + free(linkpath); + + if ((fd = open(path, O_RDONLY)) == -1) { + fprintf(stderr, "open(%s, O_RDONLY) failed: %s\n", path, strerror(errno)); + fail++; + } else { + if (fstat(fd, &st) == -1) { + perror("fstat"); + fail++; + } else if (st.st_size) { + if ((buf = malloc(st.st_size)) == NULL) + perror("malloc"); + else { + if (lseek(fd, 0, SEEK_SET) == -1) { + perror("lseek(fd, 0, SEEK_SET) failed"); + fail++; + } + if (read(fd, buf, st.st_size) != st.st_size) { + perror("read failed"); + fail++; + } + free(buf); + } + } + close(fd); + } + + errno = 0; + if (unlink(path) != -1) { + fprintf(stderr, "unlink(%s) did not fail\n", path); + fail++; + } else if (errno != EPERM) { + fprintf(stderr, "unlink(%s) did not set errno == EPERM\n", path); + fail ++; + } + + free(path); + asprintf(&path, "%s/immutable.d/file", dir); + if ((fd = open(path, O_RDWR)) == -1) { + fprintf(stderr, "open(%s, O_RDWR) failed: %s\n", path, strerror(errno)); + fail++; + } else { + if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) { + fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, strlen(scribble), strerror(errno)); + fail++; + } + close(fd); + } + if (!getuid) { + if (chmod(path, 0777) == -1) { + fprintf(stderr, "chmod(%s, 0777) failed: %s\n", path, strerror(errno)); + fail++; + } else + chmod(path, 0666); + if (chown(path, 1, 1) == -1) { + fprintf(stderr, "chown(%s, 1, 1) failed: %s\n", path, strerror(errno)); + fail++; + } else + chown(path, 0, 0); + } + + asprintf(&linkpath, "%s/immutable.d/file.link", dir); + errno = 0; + if (link(path, linkpath) != -1) { + fprintf(stderr, "link(%s, %s) did not fail\n", path, linkpath); + fail++; + unlink(linkpath); + } else if (errno != EACCES) { + fprintf(stderr, "link(%s, %s) did not set errno == EACCES\n", path, linkpath); + fail++; + } + if (symlink(path, linkpath) != -1) { + fprintf(stderr, "symlink(%s, %s) did not fail\n", path, linkpath); + fail++; + unlink(linkpath); + } else if (errno != EACCES) { + fprintf(stderr, "symlink(%s, %s) did not set errno == EACCES\n", path, linkpath); + fail++; + } + free(linkpath); + asprintf(&linkpath, "%s/immutable.d/file.newname", dir); + if (rename(path, linkpath) != -1) { + fprintf(stderr, "rename(%s, %s) did not fail\n", path, linkpath); + fail++; + rename(linkpath, path); + } else if (errno != EACCES) { + fprintf(stderr, "rename(%s, %s) did not set errno == EACCES\n", path, linkpath); + fail++; + } + free(linkpath); + + if (unlink(path) != -1) { + fprintf(stderr, "unlink(%s) did not fail\n", path); + fail++; + } else if (errno != EACCES) { + fprintf(stderr, "unlink(%s) did not set errno == EACCES\n", path); + fail++; + } + + free(path); + asprintf(&path, "%s/immutable.d/newfile", dir); + errno = 0; + if ((fd = open(path, O_RDWR|O_CREAT, 0666)) != -1) { + fprintf(stderr, "open(%s, O_RDWR|O_CREAT, 0666) did not fail\n", path); + fail++; + unlink(path); + } else if (errno != EACCES) { + fprintf(stderr, "open(%s, O_RDWR|O_CREAT, 0666) did not set errno == EACCES\n", path); + fail++; + } + if (!getuid()) { + if (stat("/dev/null", &st) != -1) { + if (mknod(path, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, st.st_rdev) != -1) { + fprintf(stderr, "mknod(%s, S_IFCHR|0666, %lld) did not fail\n", path, st.st_rdev); + fail++; + unlink(path); + } else if (errno != EACCES) { + fprintf(stderr, "mknod(%s, S_IFCHR|0666, %lld) did not set errno == EACCESS\n", path, st.st_rdev); + fail++; + } + } + } + + free(path); + asprintf(&path, "%s/immutable.d/newdir", dir); + errno = 0; + if (mkdir(path, 0777) != -1) { + fprintf(stderr, "mkdir(%s, 0777) did not fail\n", path); + fail++; + rmdir(path); + } else if (errno != EACCES) { + fprintf(stderr, "mkdir(%s, 0777) did not set errno == EACCES\n", path); + fail++; + } + + free(path); + asprintf(&path, "%s/immutable.d/dir/newfile-%d", dir, getuid()); + if ((fd = open(path, O_RDWR|O_CREAT, 0666)) == -1) { + fprintf(stderr, "open(%s, O_RDWR|O_CREAT, 0666) failed: %s\n", path, strerror(errno)); + fail++; + } else { + if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) { + fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, strlen(scribble), strerror(errno)); + fail++; + } + close(fd); + } + if (!getuid()) { + if (chmod(path, 0700) == -1) { + fprintf(stderr, "chmod(%s, 0700) failed: %s\n", path, strerror(errno)); + fail++; + } else + chmod(path, 0666); + + if (chown(path, 1, 1) == -1) { + fprintf(stderr, "chown(%s, 1, 1) failed: %s\n", path, strerror(errno)); + fail++; + } else + chown(path, 0, 0); + } + + free(path); + asprintf(&path, "%s/immutable.d/dir", dir); + errno = 0; + if (rmdir(path) != -1) { + fprintf(stderr, "rmdir(%s) did not fail\n", path); + fail++; + } else if (errno != EACCES) { + fprintf(stderr, "rmdir(%s) did not set errno == EACCES\n", path); + fail++; + } + + free(path); + asprintf(&path, "%s/immutable.d", dir); + +#ifdef TEST_UTIME + errno = 0; + if (utime(path, &tbuf) != -1) { + fprintf(stderr, "utime(%s, ) did not fail\n", path); + fail++; + } else if (errno != EPERM) { + fprintf(stderr, "utime(%s, ) did not set errno == EPERM\n", path); + fail++; + } + + errno = 0; + if (utime(path, NULL) != -1) { + fprintf(stderr, "utime(%s, NULL) did not fail\n", path); + fail++; + } else if (errno != EACCES) { + fprintf(stderr, "utime(%s, NULL) did not set errno == EACCES\n", path); + fail++; + } +#endif /* TEST_UTIME */ + + if (!getuid()) { /* these would fail if not root anyway */ + errno = 0; + if (chmod(path, 7777) != -1) { + fprintf(stderr, "chmod(%s, 7777) did not fail\n", path); + fail++; + chmod(path, 0666); + } else if (errno != EPERM) { + fprintf(stderr, "chmod(%s, 7777) did not set errno == EPERM\n", path); + fail++; + } + + errno = 0; + if (chown(path, 1, 1) != -1) { + fprintf(stderr, "chown(%s, 1, 1) did not fail\n", path); + fail++; + chown(path, 0, 0); + } else if (errno != EPERM) { + fprintf(stderr, "chown(%s, 1, 1) did not set errno to EPERM\n", path); + fail++; + } + + errno = 0; + if (del_acl(path) != 1) { + if (errno != EOPNOTSUPP) { + fprintf(stderr, "del_acl(%s) did not fail\n", path); + fail++; + } + } else if (errno != EPERM) { + fprintf(stderr, "del_acl(%s) did not set errno == EPERM\n", path); + fail++; + } + errno = 0; + if (add_acl(path, acl_text) != 1) { + if (errno != EOPNOTSUPP) { + fprintf(stderr, "add_acl(%s) did not fail\n", path); + fail++; + } + } else if (errno != EPERM) { + fprintf(stderr, "add_acl(%s) did not set errno == EPERM\n", path); + fail++; + } + + if (setxattr(path, "trusted.test", "scribble", strlen("scribble"), XATTR_REPLACE) != -1) { + fprintf(stderr, "setxattr(%s, trusted.test, scribble, 8, XATTR_REPLACE) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, + "setxattr(%s, trusted.test, scribble, 8, XATTR_REPLACE) did not set errno == EPERM\n", path); + fail++; + } + if (setxattr(path, "trusted.scribble", "scribble", strlen("scribble"), XATTR_CREATE) != -1) { + fprintf(stderr, "setxattr(%s, trusted.scribble, scribble, 8, XATTR_CREATE) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, + "setxattr(%s, trusted.scribble, scribble, 8, XATTR_CREATE) did not set errno == EPERM\n", path); + fail++; + } + if (removexattr(path, "trusted.test") != -1) { + fprintf(stderr, "removexattr(%s, trusted.test) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, + "removexattr(%s, trusted.test) did not set errno == EPERM\n", path); + fail++; + } + } + + if (setxattr(path, "user.test", "scribble", strlen("scribble"), XATTR_REPLACE) != -1) { + fprintf(stderr, "setxattr(%s, user.test, scribble, 8, XATTR_REPLACE) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, "setxattr(%s, user.test, scribble, 8, XATTR_REPLACE) did not set errno == EPERM\n", path); + fail++; + } + if (setxattr(path, "user.scribble", "scribble", strlen("scribble"), XATTR_CREATE) != -1) { + fprintf(stderr, "setxattr(%s, user.scribble, scribble, 8, XATTR_CREATE) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, "setxattr(%s, user.scribble, scribble, 8, XATTR_CREATE) did not set errno == EPERM\n", path); + fail++; + } + if (removexattr(path, "user.test") != -1) { + fprintf(stderr, "removexattr(%s, user.test) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, + "removexattr(%s, user.test) did not set errno == EPERM\n", path); + fail++; + } + + free(path); + asprintf(&path, "%s/empty-immutable.d", dir); + errno = 0; + if (rmdir(path) != -1) { + fprintf(stderr, "rmdir(%s) did not fail\n", path); + fail++; + } else if (errno != EPERM) { + fprintf(stderr, "rmdir(%s) did not set errno == EPERM\n", path); + fail++; + } + + free(path); + return fail; +} + +static int test_append(const char *dir) +{ + int fd; + char *buf; + char *orig = NULL; + char *path; + char *linkpath; + off_t origsize = 0; + int fail = 0; + struct utimbuf tbuf; + struct stat st; + struct statfs stfs; + static const char *acl_text = "u::rwx,g::rwx,o::rwx,u:daemon:rwx,m::rwx"; + static const char *scribble = "scribbled by tester\n"; + static const char *scribble2 = "scribbled by tester\nscribbled by tester\n"; + static const char *scribble4 = "scribbled by tester\nscribbled by tester\n" + "scribbled by tester\nscribbled by tester\n"; + + tbuf.actime = 0; + tbuf.modtime = 0; + + if (statfs(dir, &stfs) == -1) { + perror("statfs failed"); + return 1; + } + + asprintf(&path, "%s/append-only.f", dir); + errno = 0; + if ((fd = open(path, O_RDWR)) != -1) { + fprintf(stderr, "open(%s, O_RDWR) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EPERM) { + fprintf(stderr, "open(%s, O_RDWR) did not set errno == EPERM\n", path); + fail++; + } + + errno = 0; + if ((fd = open(path, O_WRONLY)) != -1) { + fprintf(stderr, "open(%s, O_WRONLY) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EPERM) { + fprintf(stderr, "open(%s, O_WRONLY) did not set errno == EPERM\n", path); + fail++; + } + + errno = 0; + if ((fd = open(path, O_RDWR|O_TRUNC)) != -1) { + fprintf(stderr, "open(%s, O_RDWR|O_TRUNC) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EPERM) { + fprintf(stderr, "open(%s, O_RDWR|O_TRUNC) did not set errno == EPERM\n", path); + fail++; + } + + errno = 0; + if ((fd = open(path, O_WRONLY|O_TRUNC)) != -1) { + fprintf(stderr, "open(%s, O_WRONLY|O_TRUNC) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EPERM) { + fprintf(stderr, "open(%s, O_WRONLY|O_TRUNC did not set errno == EPERM\n", path); + fail++; + } + + errno = 0; + if ((fd = open(path, O_RDWR|O_APPEND|O_TRUNC)) != -1) { + fprintf(stderr, "open(%s, O_RDWR|O_APPEND|O_TRUNC) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EPERM) { + fprintf(stderr, "open(%s, O_RDWR|O_APPEND|O_TRUNC) did not set errno == EPERM\n", path); + fail++; + } + + errno = 0; + if ((fd = open(path, O_WRONLY|O_APPEND|O_TRUNC)) != -1) { + fprintf(stderr, "open(%s, O_WRONLY|O_APPEND|O_TRUNC) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EPERM) { + fprintf(stderr, "open(%s, O_WRONLY|O_APPEND|O_TRUNC) did not set errno == EPERM\n", path); + fail++; + } + + errno = 0; + if (truncate(path, 0) != -1) { + fprintf(stderr, "truncate(%s, 0) did not fail\n", path); + fail++; + } else if (errno != EPERM) { + fprintf(stderr, "truncate(%s, 0) did not set errno == EPERM\n", path); + fail++; + } + + if (stfs.f_type == XFS_SUPER_MAGIC && !getuid()) { + jdm_fshandle_t *fshandle; + xfs_bstat_t bstat; + xfs_fsop_bulkreq_t bulkreq; + xfs_ino_t ino; + char *dirpath; + + dirpath = strdup(path); /* dirname obnoxiously modifies its arg */ + if ((fshandle = jdm_getfshandle(dirname(dirpath))) == NULL) { + perror("jdm_getfshandle"); + return 1; + } + free(dirpath); + + if (stat(path, &st) != 0) { + perror("stat"); + return 1; + } + + ino = st.st_ino; + + bulkreq.lastip = &ino; + bulkreq.icount = 1; + bulkreq.ubuffer = &bstat; + bulkreq.ocount = NULL; + + if ((fd = open(path, O_RDONLY)) == -1) { + perror("open"); + return 1; + } + + if (ioctl(fd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq) == -1) { + perror("ioctl(fd, XFS_IOC_FSBULKSTAT_SINGLE"); + close(fd); + return 1; + } + close(fd); + + errno = 0; + if ((fd = jdm_open(fshandle, &bstat, O_RDWR)) != -1) { + fprintf(stderr, "jdm_open(%s, O_RDWR) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EPERM) { + perror("jdm_open"); + fprintf(stderr, "jdm_open(%s, O_RDWR) did not set errno == EPERM\n", path); + fail++; + } + + errno = 0; + if ((fd = jdm_open(fshandle, &bstat, O_WRONLY)) != -1) { + fprintf(stderr, "jdm_open(%s, O_WRONLY) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EPERM) { + fprintf(stderr, "jdm_open(%s, O_WRONLY) did not set errno == EPERM\n", path); + fail++; + } + + errno = 0; + if ((fd = jdm_open(fshandle, &bstat, O_RDWR|O_TRUNC)) != -1) { + fprintf(stderr, "jdm_open(%s, O_RDWR|O_TRUNC) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EPERM) { + fprintf(stderr, "jdm_open(%s, O_RDWR|O_TRUNC) did not set errno == EPERM\n", path); + fail++; + } + + errno = 0; + if ((fd = jdm_open(fshandle, &bstat, O_WRONLY|O_TRUNC)) != -1) { + fprintf(stderr, "jdm_open(%s, O_WRONLY|O_TRUNC) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EPERM) { + fprintf(stderr, "jdm_open(%s, O_WRONLY|O_TRUNC did not set errno == EPERM\n", path); + fail++; + } + + errno = 0; + if ((fd = jdm_open(fshandle, &bstat, O_RDWR|O_APPEND|O_TRUNC)) != -1) { + fprintf(stderr, "jdm_open(%s, O_RDWR|O_APPEND|O_TRUNC) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EPERM) { + fprintf(stderr, "jdm_open(%s, O_RDWR|O_APPEND|O_TRUNC) did not set errno == EPERM\n", path); + fail++; + } + + errno = 0; + if ((fd = jdm_open(fshandle, &bstat, O_WRONLY|O_APPEND|O_TRUNC)) != -1) { + fprintf(stderr, "jdm_open(%s, O_WRONLY|O_APPEND|O_TRUNC) did not fail\n", path); + fail++; + close(fd); + } else if (errno != EPERM) { + fprintf(stderr, "jdm_open(%s, O_WRONLY|O_APPEND|O_TRUNC) did not set errno == EPERM\n", path); + fail++; + } + } + +#ifdef TEST_UTIME + errno = 0; + if (utime(path, &tbuf) != -1) { + fprintf(stderr, "utime(%s, ) did not fail\n", path); + fail++; + } else if (errno != EPERM) { + fprintf(stderr, "utime(%s, ) did not set errno == EPERM\n", path); + fail++; + } + + errno = 0; + if (utime(path, NULL) == -1) { + fprintf(stderr, "utime(%s, NULL) failed\n", path); + fail++; + } +#endif /* TEST_UTIME */ + + asprintf(&linkpath, "%s/append-only.f.hardlink", dir); + errno = 0; + if (link(path, linkpath) != -1) { + fprintf(stderr, "link(%s, %s) did not fail\n", path, linkpath); + fail++; + unlink(linkpath); + } else if (errno != EPERM) { + fprintf(stderr, "link(%s, %s) did not set errno == EPERM\n", path, linkpath); + fail++; + } + free(linkpath); + + if (!getuid()) { /* these would fail if not root anyway */ + errno = 0; + if (chmod(path, 7777) != -1) { + fprintf(stderr, "chmod(%s, 7777) did not fail\n", path); + fail++; + chmod(path, 0666); + } else if (errno != EPERM) { + fprintf(stderr, "chmod(%s, 7777) did not set errno == EPERM\n", path); + fail++; + } + + errno = 0; + if (chown(path, 1, 1) != -1) { + fprintf(stderr, "chown(%s, 1, 1) did not fail\n", path); + fail++; + chown(path, 0, 0); + } else if (errno != EPERM) { + fprintf(stderr, "chown(%s, 1, 1) did not set errno to EPERM\n", path); + fail++; + } + errno = 0; + if (del_acl(path) != 1) { + if (errno != EOPNOTSUPP) { + fprintf(stderr, "del_acl(%s) did not fail\n", path); + fail++; + } + } else if (errno != EPERM) { + fprintf(stderr, "del_acl(%s) did not set errno == EPERM\n", path); + fail++; + } + errno = 0; + if (add_acl(path, acl_text) != 1) { + if (errno != EOPNOTSUPP) { + fprintf(stderr, "add_acl(%s) did not fail\n", path); + fail++; + } + } else if (errno != EPERM) { + fprintf(stderr, "add_acl(%s) did not set errno == EPERM\n", path); + fail++; + } + + if (setxattr(path, "trusted.test", "scribble", strlen("scribble"), XATTR_REPLACE) != -1) { + fprintf(stderr, "setxattr(%s, trusted.test, scribble, 8, XATTR_REPLACE) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, + "setxattr(%s, trusted.test, scribble, 8, XATTR_REPLACE) did not set errno == EPERM\n", path); + fail++; + } + if (setxattr(path, "trusted.scribble", "scribble", strlen("scribble"), XATTR_CREATE) != -1) { + fprintf(stderr, "setxattr(%s, trusted.scribble, scribble, 8, XATTR_CREATE) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, + "setxattr(%s, trusted.scribble, scribble, 8, XATTR_CREATE) did not set errno == EPERM\n", path); + fail++; + } + if (removexattr(path, "trusted.test") != -1) { + fprintf(stderr, "removexattr(%s, trusted.test) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, + "removexattr(%s, trusted.test) did not set errno == EPERM\n", path); + fail++; + } + } + + if (setxattr(path, "user.test", "scribble", strlen("scribble"), XATTR_REPLACE) != -1) { + fprintf(stderr, "setxattr(%s, user.test, scribble, 8, XATTR_REPLACE) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, "setxattr(%s, user.test, scribble, 8, XATTR_REPLACE) did not set errno == EPERM\n", path); + fail++; + } + if (setxattr(path, "user.scribble", "scribble", strlen("scribble"), XATTR_CREATE) != -1) { + fprintf(stderr, "setxattr(%s, user.scribble, scribble, 8, XATTR_CREATE) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, "setxattr(%s, user.scribble, scribble, 8, XATTR_CREATE) did not set errno == EPERM\n", path); + fail++; + } + if (removexattr(path, "user.test") != -1) { + fprintf(stderr, "removexattr(%s, user.test) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, + "removexattr(%s, user.test) did not set errno == EPERM\n", path); + fail++; + } + + asprintf(&linkpath, "%s/append-only.f.newname", dir); + errno = 0; + if (rename(path, linkpath) != -1) { + fprintf(stderr, "rename(%s, %s) did not fail\n", path, linkpath); + fail++; + rename(linkpath, path); + } else if (errno != EPERM) { + fprintf(stderr, "rename(%s, %s) did not set errno == EPERM\n", path, linkpath); + fail++; + } + free(linkpath); + + if ((fd = open(path, O_RDONLY)) == -1) { + fprintf(stderr, "open(%s, O_RDONLY) failed: %s\n", path, strerror(errno)); + fail++; + } else { + if (fstat(fd, &st) == -1) { + perror("fstat"); + fail++; + } else if (st.st_size) { + origsize = st.st_size; + if ((orig = malloc(st.st_size)) == NULL) + perror("malloc"); + else { + if (lseek(fd, 0, SEEK_SET) == -1) { + perror("lseek(fd, 0, SEEK_SET) failed"); + fail++; + } + if (read(fd, orig, st.st_size) != st.st_size) { + perror("read failed"); + fail++; + } + } + } + close(fd); + } + + if ((fd = open(path, O_WRONLY|O_APPEND)) == -1) { + fprintf(stderr, "open(%s, O_WRONLY|O_APPEND) failed: %s\n", path, strerror(errno)); + fail++; + } else { + lseek(fd, 0, SEEK_SET); /* this is silently ignored */ + if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) { + fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, strlen(scribble), + strerror(errno)); + fail++; + } + lseek(fd, origsize, SEEK_SET); /* this is silently ignored */ + if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) { + fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, strlen(scribble), + strerror(errno)); + fail++; + } + close(fd); + if ((fd = open(path, O_RDONLY)) == -1) { /* now we check to make sure lseek() ignored us */ + fprintf(stderr, "open(%s, O_RDONLY) failed: %s\n", path, strerror(errno)); + fail++; + } else { + if (lseek(fd, 0, SEEK_SET) == -1) { + fprintf(stderr, "lseek(%s, 0, SEEK_SET) failed: %s\n", path, strerror(errno)); + fail++; + } else if (origsize) { + if ((buf = malloc(origsize)) == NULL) + perror("malloc"); + else { + if (read(fd, buf, origsize) == -1) { + fprintf(stderr, "read(%s, &buf, %ld) failed: %s\n", path, (long)origsize, strerror(errno)); + fail++; + } else { + if (memcmp(orig, buf, origsize)) { + fprintf(stderr, "existing data in append-only.f was overwritten\n"); + fail++; + } + } + free(buf); + } + } + if (lseek(fd, origsize, SEEK_SET) == -1) { + fprintf(stderr, "lseek(%s, %ld, SEEK_SET) failed: %s\n", path, (long)origsize, strerror(errno)); + fail++; + } else { + if ((buf = malloc(strlen(scribble2))) == NULL) + perror("malloc"); + else { + if (read(fd, buf, strlen(scribble2)) == -1) { + fprintf(stderr, "read(%s, &buf, %d) failed: %s\n", path, + strlen(scribble2), strerror(errno)); + fail++; + } else { + if (memcmp(scribble2, buf, strlen(scribble2))) { + fprintf(stderr, "existing data in append-only.f was overwritten\n"); + fail++; + } + } + free(buf); + } + close(fd); + } + } + } + + if ((fd = open(path, O_RDWR|O_APPEND)) == -1) { + fprintf(stderr, "open(%s, O_RDWR|O_APPEND) failed: %s\n", path, strerror(errno)); + fail++; + } else { + lseek(fd, 0, SEEK_SET); /* this is silently ignored */ + if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) { + fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, strlen(scribble), + strerror(errno)); + fail++; + } + lseek(fd, origsize, SEEK_SET); /* this is silently ignored */ + if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) { + fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, strlen(scribble), + strerror(errno)); + fail++; + } + close(fd); + if ((fd = open(path, O_RDONLY)) == -1) { /* now we check to make sure lseek() ignored us */ + fprintf(stderr, "open(%s, O_RDONLY) failed: %s\n", path, strerror(errno)); + fail++; + } else { + if (lseek(fd, 0, SEEK_SET) == -1) { + fprintf(stderr, "lseek(%s, 0, SEEK_SET) failed: %s\n", path, strerror(errno)); + fail++; + } else if (origsize) { + if ((buf = malloc(origsize)) == NULL) + perror("malloc"); + else { + if (read(fd, buf, origsize) == -1) { + fprintf(stderr, "read(%s, &buf, %ld) failed: %s\n", path, (long)origsize, strerror(errno)); + fail++; + } else { + if (memcmp(orig, buf, origsize)) { + fprintf(stderr, "existing data in append-only.f was overwritten\n"); + fail++; + } + } + free(buf); + free(orig); + } + } + if (lseek(fd, origsize, SEEK_SET) == -1) { + fprintf(stderr, "lseek(%s, %ld, SEEK_SET) failed: %s\n", path, (long)origsize, strerror(errno)); + fail++; + } else if (scribble4) { + if ((buf = malloc(strlen(scribble4))) == NULL) + perror("malloc"); + else { + if (read(fd, buf, strlen(scribble4)) == -1) { + fprintf(stderr, "read(%s, &buf, %d) failed: %s\n", path, + strlen(scribble4), strerror(errno)); + fail++; + } else { + if (memcmp(scribble4, buf, strlen(scribble4))) { + fprintf(stderr, "existing data in append-only.f was overwritten\n"); + fail++; + } + } + free(buf); + } + close(fd); + } + } + } + + if (stfs.f_type == XFS_SUPER_MAGIC && !getuid()) { + jdm_fshandle_t *fshandle; + xfs_bstat_t bstat; + xfs_fsop_bulkreq_t bulkreq; + xfs_ino_t ino; + char *dirpath; + + if ((fd = open(path, O_RDONLY)) == -1) { + fprintf(stderr, "open(%s, O_RDONLY) failed: %s\n", path, strerror(errno)); + fail++; + } else { + if (fstat(fd, &st) == -1) { + perror("fstat"); + fail++; + } else if (st.st_size) { + origsize = st.st_size; + if ((orig = malloc(st.st_size)) == NULL) + perror("malloc"); + else { + if (lseek(fd, 0, SEEK_SET) == -1) { + perror("lseek(fd, 0, SEEK_SET) failed"); + fail++; + } + if (read(fd, orig, st.st_size) != st.st_size) { + perror("read failed"); + fail++; + } + } + } + close(fd); + } + + dirpath = strdup(path); /* dirname obnoxiously modifies its arg */ + if ((fshandle = jdm_getfshandle(dirname(dirpath))) == NULL) { + perror("jdm_getfshandle"); + return 1; + } + free(dirpath); + + if (stat(path, &st) != 0) { + perror("stat"); + return 1; + } + + ino = st.st_ino; + + bulkreq.lastip = &ino; + bulkreq.icount = 1; + bulkreq.ubuffer = &bstat; + bulkreq.ocount = NULL; + + if ((fd = open(path, O_RDONLY)) == -1) { + perror("open"); + return 1; + } + + if (ioctl(fd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq) == -1) { + perror("ioctl(fd, XFS_IOC_FSBULKSTAT_SINGLE"); + close(fd); + return 1; + } + close(fd); + + if ((fd = jdm_open(fshandle, &bstat, O_WRONLY|O_APPEND)) == -1) { + fprintf(stderr, "jdm_open(%s, O_WRONLY|O_APPEND) failed: %s\n", path, strerror(errno)); + fail++; + } else { + lseek(fd, 0, SEEK_SET); /* this is silently ignored */ + if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) { + fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, strlen(scribble), + strerror(errno)); + fail++; + } + lseek(fd, origsize, SEEK_SET); /* this is silently ignored */ + if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) { + fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, strlen(scribble), + strerror(errno)); + fail++; + } + close(fd); + if ((fd = jdm_open(fshandle, &bstat, O_RDONLY)) == -1) { /* now we check to make sure lseek() ignored us */ + fprintf(stderr, "jdm_open(%s, O_RDONLY) failed: %s\n", path, strerror(errno)); + fail++; + } else { + if (lseek(fd, 0, SEEK_SET) == -1) { + fprintf(stderr, "lseek(%s, 0, SEEK_SET) failed: %s\n", path, strerror(errno)); + fail++; + } else if (origsize) { + if ((buf = malloc(origsize)) == NULL) + perror("malloc"); + else { + if (read(fd, buf, origsize) == -1) { + fprintf(stderr, "read(%s, &buf, %ld) failed: %s\n", path, (long)origsize, strerror(errno)); + fail++; + } else { + if (memcmp(orig, buf, origsize)) { + fprintf(stderr, "existing data in append-only.f was overwritten\n"); + fail++; + } + } + free(buf); + } + } + if (lseek(fd, origsize, SEEK_SET) == -1) { + fprintf(stderr, "lseek(%s, %ld, SEEK_SET) failed: %s\n", path, (long)origsize, strerror(errno)); + fail++; + } else if (strlen(scribble2)) { + if ((buf = malloc(strlen(scribble2))) == NULL) + perror("malloc"); + else { + if (read(fd, buf, strlen(scribble2)) == -1) { + fprintf(stderr, "read(%s, &buf, %d) failed: %s\n", path, + strlen(scribble2), strerror(errno)); + fail++; + } else { + if (memcmp(scribble2, buf, strlen(scribble2))) { + fprintf(stderr, "existing data in append-only.f was overwritten\n"); + fail++; + } + } + free(buf); + } + close(fd); + } + } + } + + if ((fd = jdm_open(fshandle, &bstat, O_RDWR|O_APPEND)) == -1) { + fprintf(stderr, "jdm_open(%s, O_RDWR|O_APPEND) failed: %s\n", path, strerror(errno)); + fail++; + } else { + lseek(fd, 0, SEEK_SET); /* this is silently ignored */ + if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) { + fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, strlen(scribble), + strerror(errno)); + fail++; + } + lseek(fd, origsize, SEEK_SET); /* this is silently ignored */ + if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) { + fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, strlen(scribble), + strerror(errno)); + fail++; + } + close(fd); + if ((fd = jdm_open(fshandle, &bstat, O_RDONLY)) == -1) { /* now we check to make sure lseek() ignored us */ + fprintf(stderr, "jdm_open(%s, O_RDONLY) failed: %s\n", path, strerror(errno)); + fail++; + } else { + if (lseek(fd, 0, SEEK_SET) == -1) { + fprintf(stderr, "lseek(%s, 0, SEEK_SET) failed: %s\n", path, strerror(errno)); + fail++; + } else if (origsize) { + if ((buf = malloc(origsize)) == NULL) + perror("malloc"); + else { + if (read(fd, buf, origsize) == -1) { + fprintf(stderr, "read(%s, &buf, %ld) failed: %s\n", path, (long)origsize, strerror(errno)); + fail++; + } else { + if (memcmp(orig, buf, origsize)) { + fprintf(stderr, "existing data in append-only.f was overwritten\n"); + fail++; + } + } + free(buf); + free(orig); + } + } + if (lseek(fd, origsize, SEEK_SET) == -1) { + fprintf(stderr, "lseek(%s, %ld, SEEK_SET) failed: %s\n", path, (long)origsize, strerror(errno)); + fail++; + } else if (strlen(scribble4)) { + if ((buf = malloc(strlen(scribble4))) == NULL) + perror("malloc"); + else { + if (read(fd, buf, strlen(scribble4)) == -1) { + fprintf(stderr, "read(%s, &buf, %d) failed: %s\n", path, + strlen(scribble4), strerror(errno)); + fail++; + } else { + if (memcmp(scribble4, buf, strlen(scribble4))) { + fprintf(stderr, "existing data in append-only.f was overwritten\n"); + fail++; + } + } + free(buf); + } + close(fd); + } + } + } + } + + errno = 0; + if (unlink(path) != -1) { + fprintf(stderr, "unlink(%s) did not fail\n", path); + fail++; + } else if (errno != EPERM) { + fprintf(stderr, "unlink(%s) did not set errno == EPERM\n", path); + fail ++; + } + + free(path); + asprintf(&path, "%s/append-only.d/file", dir); + if ((fd = open(path, O_RDWR)) == -1) { + fprintf(stderr, "open(%s, O_RDWR) failed: %s\n", path, strerror(errno)); + fail++; + } else { + if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) { + fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, strlen(scribble), strerror(errno)); + fail++; + } + close(fd); + } + if (!getuid) { + if (chmod(path, 0777) == -1) { + fprintf(stderr, "chmod(%s, 0777) failed: %s\n", path, strerror(errno)); + fail++; + } else + chmod(path, 0666); + if (chown(path, 1, 1) == -1) { + fprintf(stderr, "chown(%s, 1, 1) failed: %s\n", path, strerror(errno)); + fail++; + } else + chown(path, 0, 0); + } + + asprintf(&linkpath, "%s/append-only.d/file.link-%d", dir, getuid()); + errno = 0; + if (link(path, linkpath) == -1) { + fprintf(stderr, "link(%s, %s) failed: %s\n", path, linkpath, strerror(errno)); + fail++; + } else if (unlink(linkpath) != -1) { + fprintf(stderr, "unlink(%s) did not fail\n", linkpath); + fail++; + } + free(linkpath); + asprintf(&linkpath, "%s/append-only.d/file.symlink-%d", dir, getuid()); + if (symlink(path, linkpath) == -1) { + fprintf(stderr, "symlink(%s, %s) failed: %s\n", path, linkpath, strerror(errno)); + fail++; + } else if (unlink(linkpath) != -1) { + fprintf(stderr, "unlink(%s) did not fail\n", linkpath); + fail++; + } + + free(linkpath); + asprintf(&linkpath, "%s/append-only.d/file.newname", dir); + if (rename(path, linkpath) != -1) { + fprintf(stderr, "rename(%s, %s) did not fail\n", path, linkpath); + fail++; + rename(linkpath, path); + } else if (errno != EPERM) { + fprintf(stderr, "rename(%s, %s) did not set errno == EPERM\n", path, linkpath); + fail++; + } + free(linkpath); + + if (unlink(path) != -1) { + fprintf(stderr, "unlink(%s) did not fail\n", path); + fail++; + } else if (errno != EPERM) { + fprintf(stderr, "unlink(%s) did not set errno == EPERM\n", path); + fail++; + } + + free(path); + asprintf(&path, "%s/append-only.d/newfile-%d", dir, getuid()); + errno = 0; + if ((fd = open(path, O_RDWR|O_CREAT, 0666)) == -1) { + fprintf(stderr, "open(%s, O_RDWR|O_CREAT, 0666) failed: %s\n", path, strerror(errno)); + fail++; + } else if (unlink(path) != -1) { + fprintf(stderr, "unlink(%s) did not fail\n", path); + fail++; + close(fd); + } else + close(fd); + + if (!getuid()) { + free(path); + asprintf(&path, "%s/append-only.d/newdev-%d", dir, getuid()); + if (stat("/dev/null", &st) != -1) { + if (mknod(path, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, st.st_rdev) == -1) { + fprintf(stderr, "mknod(%s, S_IFCHR|0666, %lld) failed: %s\n", path, st.st_rdev, strerror(errno)); + fail++; + } else if (unlink(path) != -1) { + fprintf(stderr, "unlink(%s) did not fail\n", path); + fail++; + } + } + } + + free(path); + asprintf(&path, "%s/append-only.d/newdir-%d", dir, getuid()); + if (mkdir(path, 0777) == -1) { + fprintf(stderr, "mkdir(%s, 0777) failed: %s\n", path, strerror(errno)); + fail++; + } else if (rmdir(path) != -1) { + fprintf(stderr, "rmdir(%s) did not fail\n", path); + fail++; + } + + free(path); + asprintf(&path, "%s/append-only.d/newdir-%d/newfile-%d", dir, getuid(), getuid()); + if ((fd = open(path, O_RDWR|O_CREAT, 0666)) == -1) { + fprintf(stderr, "open(%s, O_RDWR|O_CREAT, 0666) failed: %s\n", path, strerror(errno)); + fail++; + } else { + if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) { + fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, strlen(scribble), strerror(errno)); + fail++; + } + close(fd); + } + if (!getuid()) { + if (chmod(path, 0700) == -1) { + fprintf(stderr, "chmod(%s, 0700) failed: %s\n", path, strerror(errno)); + fail++; + } else + chmod(path, 0666); + + if (chown(path, 1, 1) == -1) { + fprintf(stderr, "chown(%s, 1, 1) failed: %s\n", path, strerror(errno)); + fail++; + } else + chown(path, 0, 0); + } + + free(path); + asprintf(&path, "%s/append-only.d/dir/newfile-%d", dir, getuid()); + if ((fd = open(path, O_RDWR|O_CREAT, 0666)) == -1) { + fprintf(stderr, "open(%s, O_RDWR|O_CREAT, 0666) failed: %s\n", path, strerror(errno)); + fail++; + } else { + if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) { + fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, strlen(scribble), strerror(errno)); + fail++; + } + close(fd); + } + if (!getuid()) { + if (chmod(path, 0700) == -1) { + fprintf(stderr, "chmod(%s, 0700) failed: %s\n", path, strerror(errno)); + fail++; + } else + chmod(path, 0666); + + if (chown(path, 1, 1) == -1) { + fprintf(stderr, "chown(%s, 1, 1) failed: %s\n", path, strerror(errno)); + fail++; + } else + chown(path, 0, 0); + } + + free(path); + asprintf(&path, "%s/append-only.d/dir", dir); + errno = 0; + if (rmdir(path) != -1) { + fprintf(stderr, "rmdir(%s) did not fail\n", path); + fail++; + } else if (errno != EPERM) { + fprintf(stderr, "rmdir(%s) did not set errno == EPERM\n", path); + fail++; + } + + free(path); + asprintf(&path, "%s/append-only.d", dir); +#ifdef TEST_UTIME + errno = 0; + if (utime(path, &tbuf) != -1) { + fprintf(stderr, "utime(%s, ) did not fail\n", path); + fail++; + } else if (errno != EPERM) { + fprintf(stderr, "utime(%s, ) did not set errno == EPERM\n", path); + fail++; + } + + errno = 0; + if (utime(path, NULL) == -1) { + fprintf(stderr, "utime(%s, NULL) failed\n", path); + fail++; + } +#endif /* TEST_UTIME */ + + if (!getuid()) { /* these would fail if not root anyway */ + errno = 0; + if (chmod(path, 7777) != -1) { + fprintf(stderr, "chmod(%s, 7777) did not fail\n", path); + fail++; + chmod(path, 0666); + } else if (errno != EPERM) { + fprintf(stderr, "chmod(%s, 7777) did not set errno == EPERM\n", path); + fail++; + } + + errno = 0; + if (chown(path, 1, 1) != -1) { + fprintf(stderr, "chown(%s, 1, 1) did not fail\n", path); + fail++; + chown(path, 0, 0); + } else if (errno != EPERM) { + fprintf(stderr, "chown(%s, 1, 1) did not set errno to EPERM\n", path); + fail++; + } + errno = 0; + if (del_acl(path) != 1) { + if (errno != EOPNOTSUPP) { + fprintf(stderr, "del_acl(%s) did not fail\n", path); + fail++; + } + } else if (errno != EPERM) { + fprintf(stderr, "del_acl(%s) did not set errno == EPERM\n", path); + fail++; + } + errno = 0; + if (add_acl(path, acl_text) != 1) { + if (errno != EOPNOTSUPP) { + fprintf(stderr, "add_acl(%s) did not fail\n", path); + fail++; + } + } else if (errno != EPERM) { + fprintf(stderr, "add_acl(%s) did not set errno == EPERM\n", path); + fail++; + } + + if (setxattr(path, "trusted.test", "scribble", strlen("scribble"), XATTR_REPLACE) != -1) { + fprintf(stderr, "setxattr(%s, trusted.test, scribble, 8, XATTR_REPLACE) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, + "setxattr(%s, trusted.test, scribble, 8, XATTR_REPLACE) did not set errno == EPERM\n", path); + fail++; + } + if (setxattr(path, "trusted.scribble", "scribble", strlen("scribble"), XATTR_CREATE) != -1) { + fprintf(stderr, "setxattr(%s, trusted.scribble, scribble, 8, XATTR_CREATE) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, + "setxattr(%s, trusted.scribble, scribble, 8, XATTR_CREATE) did not set errno == EPERM\n", path); + fail++; + } + if (removexattr(path, "trusted.test") != -1) { + fprintf(stderr, "removexattr(%s, trusted.test) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, + "removexattr(%s, trusted.test) did not set errno == EPERM\n", path); + fail++; + } + } + + if (setxattr(path, "user.test", "scribble", strlen("scribble"), XATTR_REPLACE) != -1) { + fprintf(stderr, "setxattr(%s, user.test, scribble, 8, XATTR_REPLACE) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, "setxattr(%s, user.test, scribble, 8, XATTR_REPLACE) did not set errno == EPERM\n", path); + fail++; + } + if (setxattr(path, "user.scribble", "scribble", strlen("scribble"), XATTR_CREATE) != -1) { + fprintf(stderr, "setxattr(%s, user.scribble, scribble, 8, XATTR_CREATE) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + fprintf(stderr, "setxattr(%s, user.scribble, scribble, 8, XATTR_CREATE) did not set errno == EPERM\n", path); + fail++; + } + if (removexattr(path, "user.test") != -1) { + fprintf(stderr, "removexattr(%s, user.test) did not fail\n", path); + fail++; + } else if (errno != EPERM && errno != EOPNOTSUPP) { + perror("removexattr"); + fprintf(stderr, + "removexattr(%s, user.test) did not set errno == EPERM\n", path); + fail++; + } + + free(path); + asprintf(&path, "%s/empty-append-only.d", dir); + errno = 0; + if (rmdir(path) != -1) { + fprintf(stderr, "rmdir(%s) did not fail\n", path); + fail++; + } else if (errno != EPERM) { + fprintf(stderr, "rmdir(%s) did not set errno == EPERM\n", path); + fail++; + } + + free(path); + return fail; +} + +static int check_test_area(const char *dir) +{ + char *path; + struct stat st; + + asprintf(&path, "%s/", dir); + if (stat(path, &st) == -1) { + fprintf(stderr, "%s: %s: %s\n", __progname, path, strerror(errno)); + return 1; + } + if (!(st.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH)) || st.st_uid) { + fprintf(stderr, "%s: %s needs to be rwx for for all, and owner uid should be 0\n", + __progname, path); + return 1; + } + + free(path); + asprintf(&path, "%s/immutable.f", dir); + if (stat(path, &st) == -1) { + perror(path); + return 1; + } + if (!(st.st_mode & (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) || st.st_uid || !S_ISREG(st.st_mode)) { + fprintf(stderr, "%s: %s needs to be a regular file, rw for all, and owner uid should be 0\n", + __progname, path); + return 1; + } + + free(path); + asprintf(&path, "%s/append-only.f", dir); + if (stat(path, &st) == -1) { + perror(path); + return 1; + } + if (!(st.st_mode & (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) || st.st_uid || !S_ISREG(st.st_mode)) { + fprintf(stderr, "%s: %s needs to be a regular file, rw for all, and owner uid should be 0\n", + __progname, path); + return 1; + } + + free(path); + asprintf(&path, "%s/immutable.d", dir); + if (stat(path, &st) == -1) { + perror(path); + return 1; + } + if (!(st.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH)) || + st.st_uid || !S_ISDIR(st.st_mode)) { + fprintf(stderr, "%s: %s needs to be a directory, rwx for all, and owner uid should be 0\n", + __progname, path); + return 1; + } + + free(path); + asprintf(&path, "%s/append-only.d", dir); + if (stat(path, &st) == -1) { + perror(path); + return 1; + } + if (!(st.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH)) || + st.st_uid || !S_ISDIR(st.st_mode)) { + fprintf(stderr, "%s: %s needs to be a directory, rwx for all, and owner uid should be 0\n", + __progname, path); + return 1; + } + + free(path); + asprintf(&path, "%s/immutable.d/file", dir); + if (stat(path, &st) == -1) { + perror(path); + return 1; + } + if (!(st.st_mode & (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) || st.st_uid || !S_ISREG(st.st_mode)) { + fprintf(stderr, "%s: %s needs to be a regular file, rw for all, and owner uid should be 0\n", + __progname, path); + return 1; + } + + free(path); + asprintf(&path, "%s/append-only.d/file", dir); + if (stat(path, &st) == -1) { + perror(path); + return 1; + } + if (!(st.st_mode & (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) || st.st_uid || !S_ISREG(st.st_mode)) { + fprintf(stderr, "%s: %s needs to be a regular file, rw for all, and owner uid should be 0\n", + __progname, path); + return 1; + } + + free(path); + asprintf(&path, "%s/immutable.d/dir", dir); + if (stat(path, &st) == -1) { + perror(path); + return 1; + } + if (!(st.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH)) || + st.st_uid || !S_ISDIR(st.st_mode)) { + fprintf(stderr, "%s: %s needs to be a directory, rwx for all, and owner uid should be 0\n", + __progname, path); + return 1; + } + + free(path); + asprintf(&path, "%s/append-only.d/dir", dir); + if (stat(path, &st) == -1) { + perror(path); + return 1; + } + if (!(st.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH)) || + st.st_uid || !S_ISDIR(st.st_mode)) { + fprintf(stderr, "%s: %s needs to be a directory, rwx for all, and owner uid should be 0\n", + __progname, path); + return 1; + } + return 0; +} + +static int create_test_area(const char *dir) +{ + int fd; + char *path; + static const char *acl_u_text = "u::rw-,g::rw-,o::rw-,u:nobody:rw-,m::rw-"; + static const char *acl_u_text_d = "u::rwx,g::rwx,o::rwx,u:nobody:rwx,m::rwx"; + struct stat st; + static const char *immutable = "This is an immutable file.\nIts contents cannot be altered.\n"; + static const char *append_only = "This is an append-only file.\nIts contents cannot be altered.\n" + "Data can only be appended.\n---\n"; + + if (getuid()) { + fprintf(stderr, "%s: you are not root, go away.\n", __progname); + return 1; + } + + if (stat(dir, &st) == 0) { + fprintf(stderr, "%s: Test area directory %s must not exist for test area creation.\n", + __progname, dir); + return 1; + } + + umask(0000); + if (mkdir(dir, 0777) != 0) { + fprintf(stderr, "%s: error creating directory %s: %s\n", __progname, dir, strerror(errno)); + return 1; + } + + asprintf(&path, "%s/immutable.d", dir); + if (mkdir(path, 0777) != 0) { + fprintf(stderr, "%s: error creating directory %s: %s\n", __progname, path, strerror(errno)); + return 1; + } + free(path); + + asprintf(&path, "%s/empty-immutable.d", dir); + if (mkdir(path, 0777) != 0) { + fprintf(stderr, "%s: error creating directory %s: %s\n", __progname, path, strerror(errno)); + return 1; + } + free(path); + + asprintf(&path, "%s/append-only.d", dir); + if (mkdir(path, 0777) != 0) { + fprintf(stderr, "%s: error creating directory %s: %s\n", __progname, path, strerror(errno)); + return 1; + } + free(path); + + asprintf(&path, "%s/empty-append-only.d", dir); + if (mkdir(path, 0777) != 0) { + fprintf(stderr, "%s: error creating directory %s: %s\n", __progname, path, strerror(errno)); + return 1; + } + free(path); + + asprintf(&path, "%s/immutable.d/dir", dir); + if (mkdir(path, 0777) != 0) { + fprintf(stderr, "%s: error creating directory %s: %s\n", __progname, path, strerror(errno)); + return 1; + } + free(path); + + asprintf(&path, "%s/append-only.d/dir", dir); + if (mkdir(path, 0777) != 0) { + fprintf(stderr, "%s: error creating directory %s: %s\n", __progname, path, strerror(errno)); + return 1; + } + free(path); + + asprintf(&path, "%s/append-only.d/file", dir); + if ((fd = open(path, O_WRONLY|O_CREAT|O_EXCL, 0666)) == -1) { + fprintf(stderr, "%s: error creating file %s: %s\n", __progname, path, strerror(errno)); + return 1; + } + close(fd); + free(path); + + asprintf(&path, "%s/immutable.d/file", dir); + if ((fd = open(path, O_WRONLY|O_CREAT|O_EXCL, 0666)) == -1) { + fprintf(stderr, "%s: error creating file %s: %s\n", __progname, path, strerror(errno)); + return 1; + } + close(fd); + free(path); + + asprintf(&path, "%s/immutable.f", dir); + if ((fd = open(path, O_WRONLY|O_CREAT|O_EXCL, 0666)) == -1) { + fprintf(stderr, "%s: error creating file %s: %s\n", __progname, path, strerror(errno)); + return 1; + } + if (write(fd, immutable, strlen(immutable)) != strlen(immutable)) { + fprintf(stderr, "%s: error writing file %s: %s\n", __progname, path, strerror(errno)); + return 1; + } + if (fadd_acl(fd, acl_u_text)) { + perror("acl"); + return 1; + } + if (fsetxattr(fd, "trusted.test", "readonly", strlen("readonly"), XATTR_CREATE) != 0) { + if (errno != EOPNOTSUPP) { + perror("setxattr"); + return 1; + } + } + if (fsetxattr(fd, "user.test", "readonly", strlen("readonly"), XATTR_CREATE) != 0) { + if (errno != EOPNOTSUPP) { + perror("setxattr"); + return 1; + } + } + if (fsetflag(path, fd, 1, 1)) { + perror("fsetflag"); + close(fd); + return 1; + } + close(fd); + free(path); + + asprintf(&path, "%s/append-only.f", dir); + if ((fd = open(path, O_WRONLY|O_CREAT|O_EXCL, 0666)) == -1) { + fprintf(stderr, "%s: error creating file %s: %s\n", __progname, path, strerror(errno)); + return 1; + } + if (write(fd, append_only, strlen(append_only)) != strlen(append_only)) { + fprintf(stderr, "%s: error writing file %s: %s\n", __progname, path, strerror(errno)); + return 1; + } + if (fadd_acl(fd, acl_u_text)) { + perror("acl"); + return 1; + } + if (fsetxattr(fd, "trusted.test", "readonly", strlen("readonly"), XATTR_CREATE) != 0) { + if (errno != EOPNOTSUPP) { + perror("setxattr"); + return 1; + } + } + if (fsetxattr(fd, "user.test", "readonly", strlen("readonly"), XATTR_CREATE) != 0) { + if (errno != EOPNOTSUPP) { + perror("setxattr"); + return 1; + } + } + if (fsetflag(path, fd, 1, 0)) { + perror("fsetflag"); + close(fd); + return 1; + } + close(fd); + free(path); + + asprintf(&path, "%s/immutable.d", dir); + if ((fd = open(path, O_RDONLY)) == -1) { + fprintf(stderr, "%s: error opening %s: %s\n", __progname, path, strerror(errno)); + return 1; + } + if (fadd_acl(fd, acl_u_text_d)) { + perror("acl"); + return 1; + } + if (fsetxattr(fd, "trusted.test", "readonly", strlen("readonly"), XATTR_CREATE) != 0) { + if (errno != EOPNOTSUPP) { + perror("setxattr"); + return 1; + } + } + if (fsetxattr(fd, "user.test", "readonly", strlen("readonly"), XATTR_CREATE) != 0) { + if (errno != EOPNOTSUPP) { + perror("setxattr"); + return 1; + } + } + if (fsetflag(path, fd, 1, 1)) { + perror("fsetflag"); + close(fd); + return 1; + } + close(fd); + free(path); + + asprintf(&path, "%s/empty-immutable.d", dir); + if ((fd = open(path, O_RDONLY)) == -1) { + fprintf(stderr, "%s: error opening %s: %s\n", __progname, path, strerror(errno)); + return 1; + } + if (fsetflag(path, fd, 1, 1)) { + perror("fsetflag"); + close(fd); + return 1; + } + close(fd); + free(path); + + asprintf(&path, "%s/append-only.d", dir); + if ((fd = open(path, O_RDONLY)) == -1) { + fprintf(stderr, "%s: error opening %s: %s\n", __progname, path, strerror(errno)); + return 1; + } + if (fadd_acl(fd, acl_u_text_d)) { + perror("acl"); + return 1; + } + if (fsetxattr(fd, "trusted.test", "readonly", strlen("readonly"), XATTR_CREATE) != 0) { + if (errno != EOPNOTSUPP) { + perror("setxattr"); + return 1; + } + } + if (fsetxattr(fd, "user.test", "readonly", strlen("readonly"), XATTR_CREATE) != 0) { + if (errno != EOPNOTSUPP) { + perror("setxattr"); + return 1; + } + } + if (fsetflag(path, fd, 1, 0)) { + perror("fsetflag"); + close(fd); + return 1; + } + close(fd); + free(path); + + asprintf(&path, "%s/empty-append-only.d", dir); + if ((fd = open(path, O_RDONLY)) == -1) { + fprintf(stderr, "%s: error opening %s: %s\n", __progname, path, strerror(errno)); + return 1; + } + if (fsetflag(path, fd, 1, 0)) { + perror("fsetflag"); + close(fd); + return 1; + } + close(fd); + free(path); + return 0; +} + +static int remove_test_area(const char *dir) +{ + int fd; + int ret; + int err = 0; + char *path; + pid_t pid; + struct stat st; + + if (getuid()) { + fprintf(stderr, "%s: you are not root, go away.\n", __progname); + return 1; + } + + if (stat(dir, &st) == -1) { + fprintf(stderr, "%s: cannot remove test area %s: %s\n", __progname, dir, strerror(errno)); + return 1; + } + + asprintf(&path, "%s/immutable.d", dir); + if ((fd = open(path, O_RDONLY)) == -1) { + fprintf(stderr, "%s: error opening %s: %s\n", __progname, path, strerror(errno)); + err = 1; + } else { + if (fsetflag(path, fd, 0, 1)) + perror("fsetflag"); + close(fd); + } + free(path); + + asprintf(&path, "%s/empty-immutable.d", dir); + if ((fd = open(path, O_RDONLY)) == -1) { + fprintf(stderr, "%s: error opening %s: %s\n", __progname, path, strerror(errno)); + err = 1; + } else { + if (fsetflag(path, fd, 0, 1)) + perror("fsetflag"); + + close(fd); + } + free(path); + + asprintf(&path, "%s/append-only.d", dir); + if ((fd = open(path, O_RDONLY)) == -1) { + fprintf(stderr, "%s: error opening %s: %s\n", __progname, path, strerror(errno)); + err = 1; + } else { + if (fsetflag(path, fd, 0, 0)) + perror("fsetflag"); + close(fd); + } + free(path); + + asprintf(&path, "%s/empty-append-only.d", dir); + if ((fd = open(path, O_RDONLY)) == -1) { + fprintf(stderr, "%s: error opening %s: %s\n", __progname, path, strerror(errno)); + err = 1; + } else { + if (fsetflag(path, fd, 0, 0)) + perror("fsetflag"); + close(fd); + } + free(path); + + asprintf(&path, "%s/append-only.f", dir); + if ((fd = open(path, O_RDONLY)) == -1) { + fprintf(stderr, "%s: error opening %s: %s\n", __progname, path, strerror(errno)); + err = 1; + } else { + if (fsetflag(path, fd, 0, 0)) + perror("fsetflag"); + + close(fd); + } + free(path); + + asprintf(&path, "%s/immutable.f", dir); + if ((fd = open(path, O_RDONLY)) == -1) { + fprintf(stderr, "%s: error opening %s: %s\n", __progname, path, strerror(errno)); + err = 1; + } else { + if (fsetflag(path, fd, 0, 1)) + perror("fsetflag"); + close(fd); + } + free(path); + + if (err) { + fprintf(stderr, "%s: Warning, expected parts of the test area missing, not removing.\n", __progname); + return 1; + } + + pid = fork(); + if (!pid) { + execl("/bin/rm", "rm", "-rf", dir, NULL); + return 1; + } else if (pid == -1) { + perror("fork failed"); + return 1; + } + wait(&ret); + + return WEXITSTATUS(ret); +} + +int main(int argc, char **argv) +{ + int ret; + int failed = 0; + +/* this arg parsing is gross, but who cares, its a test program */ + + if (argc < 2) { + fprintf(stderr, "usage: t_immutable [-C|-c|-r] test_area_dir\n"); + return 1; + } + + if (!strcmp(argv[1], "-c")) { + if (argc == 3) { + if ((ret = create_test_area(argv[argc-1]))) + return ret; + } else { + fprintf(stderr, "usage: t_immutable -c test_area_dir\n"); + return 1; + } + } else if (!strcmp(argv[1], "-C")) { + if (argc == 3) { + return create_test_area(argv[argc-1]); + } else { + fprintf(stderr, "usage: t_immutable -C test_area_dir\n"); + return 1; + } + } else if (!strcmp(argv[1], "-r")) { + if (argc == 3) + return remove_test_area(argv[argc-1]); + else { + fprintf(stderr, "usage: t_immutable -r test_area_dir\n"); + return 1; + } + } else if (argc != 2) { + fprintf(stderr, "usage: t_immutable [-c|-r] test_area_dir\n"); + return 1; + } + + umask(0000); + + if (check_test_area(argv[argc-1])) + return 1; + + printf("testing immutable..."); + if ((ret = test_immutable(argv[argc-1])) != 0) { + printf("FAILED! (%d tests failed)\n", ret); + failed = 1; + } else + puts("PASS."); + + printf("testing append-only..."); + if ((ret = test_append(argv[argc-1])) != 0) { + printf("FAILED! (%d tests failed)\n", ret); + failed = 1; + } else + puts("PASS."); + + if (!getuid() && !failed) { + if (setgroups(0, NULL) != 0) + perror("setgroups"); + if (setgid(65534) != 0) + perror("setgid"); + if (setuid(65534) != 0) + perror("setuid"); + printf("testing immutable as non-root..."); + if ((ret = test_immutable(argv[argc-1])) != 0) { + printf("FAILED! (%d tests failed)\n", ret); + failed = 1; + } else + puts("PASS."); + + printf("testing append-only as non-root..."); + if ((ret = test_append(argv[argc-1])) != 0) { + printf("FAILED! (%d tests failed)\n", ret); + failed = 1; + } else + puts("PASS."); + } + + return failed; +}