| /* 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 (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; | 
 | } |