blob: 7a7195e43f88f2e717f5c86466b317425cff1e09 [file] [log] [blame]
/* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/vfs.h>
#include <utime.h>
#include <errno.h>
#include <grp.h>
#include <libgen.h>
#include <sys/acl.h>
#include <attr/xattr.h>
#include <linux/fs.h>
#include <linux/magic.h>
#include <xfs/xfs.h>
#include <xfs/handle.h>
#include <xfs/jdm.h>
#ifndef XFS_SUPER_MAGIC
#define XFS_SUPER_MAGIC 0x58465342
#endif
extern const char *__progname;
static int fsetflag(const char *path, int fd, int on, int immutable)
{
#ifdef FS_IOC_SETFLAGS
int fsflags = 0;
int fsfl;
if (ioctl(fd, FS_IOC_GETFLAGS, &fsflags) < 0) {
close(fd);
return 1;
}
if (immutable)
fsfl = FS_IMMUTABLE_FL;
else
fsfl = FS_APPEND_FL;
if (on)
fsflags |= fsfl;
else
fsflags &= ~fsfl;
if (ioctl(fd, FS_IOC_SETFLAGS, &fsflags) < 0) {
close(fd);
return 1;
}
close(fd);
return 0;
#else
errno = EOPNOTSUPP;
close(fd);
return 1;
#endif
}
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 && errno != EPERM) {
fprintf(stderr, "open(%s, O_RDWR) did not set errno == EACCES or 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 != EACCES && errno != EPERM) {
fprintf(stderr, "open(%s, O_WRONLY) did not set errno == EACCES or 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 != EACCES && errno != EPERM) {
fprintf(stderr, "open(%s, O_RDWR|O_TRUNC) did not set errno == EACCES or 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 != EACCES && errno != EPERM) {
fprintf(stderr, "open(%s, O_WRONLY|O_TRUNC did not set errno == EACCES or EPERM\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 && errno != EPERM) {
fprintf(stderr, "open(%s, O_RDWR|O_APPEND) did not set errno == EACCES or EPERM\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 && errno != EPERM) {
fprintf(stderr, "open(%s, O_WRONLY|O_APPEND) did not set errno == EACCES or 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 != EACCES && errno != EPERM) {
fprintf(stderr, "open(%s, O_RDWR|O_APPEND|O_TRUNC) did not set errno == EACCES or 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 != EACCES && errno != EPERM) {
fprintf(stderr, "open(%s, O_WRONLY|O_APPEND|O_TRUNC) did not set errno == EACCES or 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 = (__u64 *)&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 && errno != EPERM) {
perror("jdm_open");
fprintf(stderr, "jdm_open(%s, O_RDWR) did not set errno == EACCES or 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 != EACCES && errno != EPERM) {
fprintf(stderr, "jdm_open(%s, O_WRONLY) did not set errno == EACCES or 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 != EACCES && errno != EPERM) {
fprintf(stderr, "jdm_open(%s, O_RDWR|O_TRUNC) did not set errno == EACCES or 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 != EACCES && errno != EPERM) {
fprintf(stderr, "jdm_open(%s, O_WRONLY|O_TRUNC did not set errno == EACCES or EPERM\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 && errno != EPERM) {
fprintf(stderr, "jdm_open(%s, O_RDWR|O_APPEND) did not set errno == EACCES or EPERM\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 && errno != EPERM) {
fprintf(stderr, "jdm_open(%s, O_WRONLY|O_APPEND) did not set errno == EACCES or 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 != EACCES && errno != EPERM) {
fprintf(stderr, "jdm_open(%s, O_RDWR|O_APPEND|O_TRUNC) did not set errno == EACCES or 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 != EACCES && errno != EPERM) {
fprintf(stderr, "jdm_open(%s, O_WRONLY|O_APPEND|O_TRUNC) did not set errno == EACCES or 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 != EACCES && errno != EPERM) {
fprintf(stderr, "truncate(%s, 0) did not set errno == EACCES or EPERM\n", path);
fail++;
}
#ifdef TEST_UTIME
errno = 0;
if (utime(path, &tbuf) != -1) {
fprintf(stderr, "utime(%s, <epoch>) did not fail\n", path);
fail++;
} else if (errno != EPERM) {
fprintf(stderr, "utime(%s, <epoch>) 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 && errno != EPERM) {
fprintf(stderr, "utime(%s, NULL) did not set errno == EACCES or EPERM\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, (int)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 && errno != EPERM) {
fprintf(stderr, "link(%s, %s) did not set errno == EACCES or EPERM\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 && errno != EPERM) {
fprintf(stderr, "symlink(%s, %s) did not set errno == EACCES or EPERM\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 && errno != EPERM) {
fprintf(stderr, "rename(%s, %s) did not set errno == EACCES or EPERM\n", path, linkpath);
fail++;
}
free(linkpath);
if (unlink(path) != -1) {
fprintf(stderr, "unlink(%s) did not fail\n", path);
fail++;
} else if (errno != EACCES && errno != EPERM) {
fprintf(stderr, "unlink(%s) did not set errno == EACCES or EPERM\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 && errno != EPERM) {
fprintf(stderr, "open(%s, O_RDWR|O_CREAT, 0666) did not set errno == EACCES or EPERM\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, (long long int)st.st_rdev);
fail++;
unlink(path);
} else if (errno != EACCES && errno != EPERM) {
fprintf(stderr, "mknod(%s, S_IFCHR|0666, %lld) did not set errno == EACCES or EPERM\n", path, (long long int)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 && errno != EPERM) {
fprintf(stderr, "mkdir(%s, 0777) did not set errno == EACCES or EPERM\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, (int)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 && errno != EPERM) {
fprintf(stderr, "rmdir(%s) did not set errno == EACCES or EPERM\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, <epoch>) did not fail\n", path);
fail++;
} else if (errno != EPERM) {
fprintf(stderr, "utime(%s, <epoch>) 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 && errno != EPERM) {
fprintf(stderr, "utime(%s, NULL) did not set errno == EACCES or EPERM\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 ((fd = open(path, O_RDWR|O_APPEND)) == -1) {
fprintf(stderr, "open(%s, O_RDWR|O_APPEND) failed: %s\n", path, strerror(errno));
fail++;
} else {
if (ftruncate(fd, 0) != -1) {
fprintf(stderr, "ftruncate(%s, 0) did not fail\n", path);
fail++;
} else if (errno != EPERM) {
fprintf(stderr, "ftruncate(%s, 0) did not set errno == EPERM\n", path);
fail++;
}
close(fd);
}
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 = (__u64 *)&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, <epoch>) did not fail\n", path);
fail++;
} else if (errno != EPERM) {
fprintf(stderr, "utime(%s, <epoch>) 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, (int)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, (int)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,
(int)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, (int)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, (int)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,
(int)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 = (__u64 *)&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, (int)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, (int)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,
(int)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, (int)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, (int)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,
(int)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, (int)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, (long long int)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, (int)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, (int)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, <epoch>) did not fail\n", path);
fail++;
} else if (errno != EPERM) {
fprintf(stderr, "utime(%s, <epoch>) 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;
}