src/t_immutable: factor out some helpers
[xfstests-dev.git] / src / t_immutable.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /* compile with: gcc -g -O0 -Wall -I/usr/include/xfs -o t_immutable t_immutable.c -lhandle -lacl -lattr */
3
4 /*
5  *  t_immutable.c - hideous test suite for immutable/append-only flags.
6  *  Copyright (C) 2003 Ethan Benson
7  */
8
9 #define TEST_UTIME
10
11 #ifndef _GNU_SOURCE
12 #define _GNU_SOURCE
13 #endif
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <sys/wait.h>
22 #include <sys/ioctl.h>
23 #include <sys/vfs.h>
24 #include <utime.h>
25 #include <errno.h>
26 #include <grp.h>
27 #include <libgen.h>
28 #include <sys/acl.h>
29 #include <sys/xattr.h>
30 #include <linux/fs.h>
31 #include <linux/magic.h>
32 #include <xfs/xfs.h>
33 #include <xfs/handle.h>
34 #include <xfs/jdm.h>
35
36 #ifndef XFS_SUPER_MAGIC
37 #define XFS_SUPER_MAGIC 0x58465342
38 #endif
39
40 extern const char *__progname;
41
42 static int fsetflag(const char *path, int fd, int on, int immutable)
43 {
44 #ifdef FS_IOC_SETFLAGS
45      int fsflags = 0;
46      int fsfl;
47
48      if (ioctl(fd, FS_IOC_GETFLAGS, &fsflags) < 0) {
49           close(fd);
50           return 1;
51      }
52      if (immutable)
53           fsfl = FS_IMMUTABLE_FL;
54      else
55           fsfl = FS_APPEND_FL;
56      if (on)
57           fsflags |= fsfl;
58      else
59           fsflags &= ~fsfl;
60      if (ioctl(fd, FS_IOC_SETFLAGS, &fsflags) < 0) {
61           close(fd);
62           return 1;
63      }
64      close(fd);
65      return 0;
66 #else
67      errno = EOPNOTSUPP;
68      close(fd);
69      return 1;
70 #endif
71 }
72
73 static int add_acl(const char *path, const char *acl_text)
74 {
75      acl_t acl;
76      int s_errno;
77      int fd;
78
79      if ((acl = acl_from_text(acl_text)) == NULL)
80           return 1;
81      if ((fd = open(path, O_RDONLY)) == -1)
82           return 1;
83      if (acl_set_fd(fd, acl))
84           if (errno != EOPNOTSUPP)
85                return 1;
86      s_errno = errno;
87      acl_free(acl);
88      close(fd);
89      errno = s_errno;
90      return 0;
91 }
92
93 static int fadd_acl(int fd, const char *acl_text)
94 {
95      acl_t acl;
96      int s_errno;
97
98      if ((acl = acl_from_text(acl_text)) == NULL)
99           perror("acl_from_text");
100      if (acl_set_fd(fd, acl))
101           if (errno != EOPNOTSUPP)
102                return 1;
103      s_errno = errno;
104      acl_free(acl);
105      errno = s_errno;
106      return 0;
107 }
108
109 static int del_acl(const char *path)
110 {
111      int fd;
112      int s_errno;
113      acl_t acl;
114      static const char *acl_text = "u::rw-,g::rw-,o::rw-";
115
116      if ((acl = acl_from_text(acl_text)) == NULL)
117           return 1;
118      if ((fd = open(path, O_RDONLY)) == -1)
119           return 1;
120      if (acl_set_fd(fd, acl))
121           if (errno != EOPNOTSUPP)
122                return 1;
123      s_errno = errno;
124      acl_free(acl);
125      close(fd);
126      errno = s_errno;
127      return 0;
128 }
129
130 static int test_immutable(const char *dir)
131 {
132      int fd;
133      char *buf;
134      char *path;
135      char *linkpath;
136      int fail = 0;
137      struct utimbuf tbuf;
138      struct stat st;
139      struct statfs stfs;
140      static const char *scribble = "scribbled by tester\n";
141      static const char *acl_text = "u::rwx,g::rwx,o::rwx,u:daemon:rwx,m::rwx";
142
143      tbuf.actime = 0;
144      tbuf.modtime = 0;
145
146      if (statfs(dir, &stfs) == -1) {
147           perror("statfs failed");
148           return 1;
149      }
150
151      asprintf(&path, "%s/immutable.f", dir);
152      errno = 0;
153      if ((fd = open(path, O_RDWR)) != -1) {
154           fprintf(stderr, "open(%s, O_RDWR) did not fail\n", path);
155           fail++;
156           close(fd);
157      } else if (errno != EACCES && errno != EPERM) {
158           fprintf(stderr, "open(%s, O_RDWR) did not set errno == EACCES or EPERM\n", path);
159           fail++;
160      }
161
162      errno = 0;
163      if ((fd = open(path, O_WRONLY)) != -1) {
164           fprintf(stderr, "open(%s, O_WRONLY) did not fail\n", path);
165           fail++;
166           close(fd);
167      } else if (errno != EACCES && errno != EPERM) {
168           fprintf(stderr, "open(%s, O_WRONLY) did not set errno == EACCES or EPERM\n", path);
169           fail++;
170      }
171
172      errno = 0;
173      if ((fd = open(path, O_RDWR|O_TRUNC)) != -1) {
174           fprintf(stderr, "open(%s, O_RDWR|O_TRUNC) did not fail\n", path);
175           fail++;
176           close(fd);
177      } else if (errno != EACCES && errno != EPERM) {
178           fprintf(stderr, "open(%s, O_RDWR|O_TRUNC) did not set errno == EACCES or EPERM\n", path);
179           fail++;
180      }
181
182      errno = 0;
183      if ((fd = open(path, O_WRONLY|O_TRUNC)) != -1) {
184           fprintf(stderr, "open(%s, O_WRONLY|O_TRUNC) did not fail\n", path);
185           fail++;
186           close(fd);
187      } else if (errno != EACCES && errno != EPERM) {
188           fprintf(stderr, "open(%s, O_WRONLY|O_TRUNC did not set errno == EACCES or EPERM\n", path);
189           fail++;
190      }
191
192      errno = 0;
193      if ((fd = open(path, O_RDWR|O_APPEND)) != -1) {
194           fprintf(stderr, "open(%s, O_RDWR|O_APPEND) did not fail\n", path);
195           fail++;
196           close(fd);
197      } else if (errno != EACCES && errno != EPERM) {
198           fprintf(stderr, "open(%s, O_RDWR|O_APPEND) did not set errno == EACCES or EPERM\n", path);
199           fail++;
200      }
201
202      errno = 0;
203      if ((fd = open(path, O_WRONLY|O_APPEND)) != -1) {
204           fprintf(stderr, "open(%s, O_WRONLY|O_APPEND) did not fail\n", path);
205           fail++;
206           close(fd);
207      } else if (errno != EACCES && errno != EPERM) {
208           fprintf(stderr, "open(%s, O_WRONLY|O_APPEND) did not set errno == EACCES or EPERM\n", path);
209           fail++;
210      }
211
212      errno = 0;
213      if ((fd = open(path, O_RDWR|O_APPEND|O_TRUNC)) != -1) {
214           fprintf(stderr, "open(%s, O_RDWR|O_APPEND|O_TRUNC) did not fail\n", path);
215           fail++;
216           close(fd);
217      } else if (errno != EACCES && errno != EPERM) {
218           fprintf(stderr, "open(%s, O_RDWR|O_APPEND|O_TRUNC) did not set errno == EACCES or EPERM\n", path);
219           fail++;
220      }
221
222      errno = 0;
223      if ((fd = open(path, O_WRONLY|O_APPEND|O_TRUNC)) != -1) {
224           fprintf(stderr, "open(%s, O_WRONLY|O_APPEND|O_TRUNC) did not fail\n", path);
225           fail++;
226           close(fd);
227      } else if (errno != EACCES && errno != EPERM) {
228           fprintf(stderr, "open(%s, O_WRONLY|O_APPEND|O_TRUNC) did not set errno == EACCES or EPERM\n", path);
229           fail++;
230      }
231
232      if (stfs.f_type == XFS_SUPER_MAGIC && !getuid()) {
233           jdm_fshandle_t *fshandle;
234           struct xfs_bstat bstat;
235           struct xfs_fsop_bulkreq  bulkreq;
236           xfs_ino_t ino;
237           char *dirpath;
238
239           dirpath = strdup(path); /* dirname obnoxiously modifies its arg */
240           if ((fshandle = jdm_getfshandle(dirname(dirpath))) == NULL) {
241                perror("jdm_getfshandle");
242                return 1;
243           }
244           free(dirpath);
245
246           if (stat(path, &st) != 0) {
247                perror("stat");
248                return 1;
249           }
250
251           ino = st.st_ino;
252
253           bulkreq.lastip = (__u64 *)&ino;
254           bulkreq.icount = 1;
255           bulkreq.ubuffer = &bstat;
256           bulkreq.ocount = NULL;
257
258           if ((fd = open(path, O_RDONLY)) == -1) {
259                perror("open");
260                return 1;
261           }
262
263           if (ioctl(fd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq) == -1) {
264                perror("ioctl(fd, XFS_IOC_FSBULKSTAT_SINGLE");
265                close(fd);
266                return 1;
267           }
268           close(fd);
269
270           errno = 0;
271           if ((fd = jdm_open(fshandle, &bstat, O_RDWR)) != -1) {
272                fprintf(stderr, "jdm_open(%s, O_RDWR) did not fail\n", path);
273                fail++;
274                close(fd);
275           } else if (errno != EACCES && errno != EPERM) {
276                perror("jdm_open");
277                fprintf(stderr, "jdm_open(%s, O_RDWR) did not set errno == EACCES or EPERM\n", path);
278                fail++;
279           }
280
281           errno = 0;
282           if ((fd = jdm_open(fshandle, &bstat, O_WRONLY)) != -1) {
283                fprintf(stderr, "jdm_open(%s, O_WRONLY) did not fail\n", path);
284                fail++;
285                close(fd);
286           } else if (errno != EACCES && errno != EPERM) {
287                fprintf(stderr, "jdm_open(%s, O_WRONLY) did not set errno == EACCES or EPERM\n", path);
288                fail++;
289           }
290
291           errno = 0;
292           if ((fd = jdm_open(fshandle, &bstat, O_RDWR|O_TRUNC)) != -1) {
293                fprintf(stderr, "jdm_open(%s, O_RDWR|O_TRUNC) did not fail\n", path);
294                fail++;
295                close(fd);
296           } else if (errno != EACCES && errno != EPERM) {
297                fprintf(stderr, "jdm_open(%s, O_RDWR|O_TRUNC) did not set errno == EACCES or EPERM\n", path);
298                fail++;
299           }
300
301           errno = 0;
302           if ((fd = jdm_open(fshandle, &bstat, O_WRONLY|O_TRUNC)) != -1) {
303                fprintf(stderr, "jdm_open(%s, O_WRONLY|O_TRUNC) did not fail\n", path);
304                fail++;
305                close(fd);
306           } else if (errno != EACCES && errno != EPERM) {
307                fprintf(stderr, "jdm_open(%s, O_WRONLY|O_TRUNC did not set errno == EACCES or EPERM\n", path);
308                fail++;
309           }
310
311           errno = 0;
312           if ((fd = jdm_open(fshandle, &bstat, O_RDWR|O_APPEND)) != -1) {
313                fprintf(stderr, "jdm_open(%s, O_RDWR|O_APPEND) did not fail\n", path);
314                fail++;
315                close(fd);
316           } else if (errno != EACCES && errno != EPERM) {
317                fprintf(stderr, "jdm_open(%s, O_RDWR|O_APPEND) did not set errno == EACCES or EPERM\n", path);
318                fail++;
319           }
320
321           errno = 0;
322           if ((fd = jdm_open(fshandle, &bstat, O_WRONLY|O_APPEND)) != -1) {
323                fprintf(stderr, "jdm_open(%s, O_WRONLY|O_APPEND) did not fail\n", path);
324                fail++;
325                close(fd);
326           } else if (errno != EACCES && errno != EPERM) {
327                fprintf(stderr, "jdm_open(%s, O_WRONLY|O_APPEND) did not set errno == EACCES or EPERM\n", path);
328                fail++;
329           }
330
331           errno = 0;
332           if ((fd = jdm_open(fshandle, &bstat, O_RDWR|O_APPEND|O_TRUNC)) != -1) {
333                fprintf(stderr, "jdm_open(%s, O_RDWR|O_APPEND|O_TRUNC) did not fail\n", path);
334                fail++;
335                close(fd);
336           } else if (errno != EACCES && errno != EPERM) {
337                fprintf(stderr, "jdm_open(%s, O_RDWR|O_APPEND|O_TRUNC) did not set errno == EACCES or EPERM\n", path);
338                fail++;
339           }
340
341           errno = 0;
342           if ((fd = jdm_open(fshandle, &bstat, O_WRONLY|O_APPEND|O_TRUNC)) != -1) {
343                fprintf(stderr, "jdm_open(%s, O_WRONLY|O_APPEND|O_TRUNC) did not fail\n", path);
344                fail++;
345                close(fd);
346           } else if (errno != EACCES && errno != EPERM) {
347                fprintf(stderr, "jdm_open(%s, O_WRONLY|O_APPEND|O_TRUNC) did not set errno == EACCES or EPERM\n", path);
348                fail++;
349           }
350      }
351
352      errno = 0;
353      if (truncate(path, 0) != -1) {
354           fprintf(stderr, "truncate(%s, 0) did not fail\n", path);
355           fail++;
356      } else if (errno != EACCES && errno != EPERM) {
357           fprintf(stderr, "truncate(%s, 0) did not set errno == EACCES or EPERM\n", path);
358           fail++;
359      }
360
361 #ifdef TEST_UTIME
362      errno = 0;
363      if (utime(path, &tbuf) != -1) {
364           fprintf(stderr, "utime(%s, <epoch>) did not fail\n", path);
365           fail++;
366      } else if (errno != EPERM) {
367           fprintf(stderr, "utime(%s, <epoch>) did not set errno == EPERM\n", path);
368           fail++;
369      }
370
371      errno = 0;
372      if (utime(path, NULL) != -1) {
373           fprintf(stderr, "utime(%s, NULL) did not fail\n", path);
374           fail++;
375      } else if (errno != EACCES && errno != EPERM) {
376           fprintf(stderr, "utime(%s, NULL) did not set errno == EACCES or EPERM\n", path);
377           fail++;
378      }
379 #endif /* TEST_UTIME */
380
381      asprintf(&linkpath, "%s/immutable.f.hardlink", dir);
382      errno = 0;
383      if (link(path, linkpath) != -1) {
384           fprintf(stderr, "link(%s, %s) did not fail\n", path, linkpath);
385           fail++;
386           unlink(linkpath);
387      } else if (errno != EPERM) {
388           fprintf(stderr, "link(%s, %s) did not set errno == EPERM\n", path, linkpath);
389           fail++;
390      }
391      free(linkpath);
392
393      if (!getuid()) { /* these would fail if not root anyway */
394           errno = 0;
395           if (chmod(path, 7777) != -1) {
396                fprintf(stderr, "chmod(%s, 7777) did not fail\n", path);
397                fail++;
398                chmod(path, 0666);
399           } else if (errno != EPERM) {
400                fprintf(stderr, "chmod(%s, 7777) did not set errno == EPERM\n", path);
401                fail++;
402           }
403
404           errno = 0;
405           if (chown(path, 1, 1) != -1) {
406                fprintf(stderr, "chown(%s, 1, 1) did not fail\n", path);
407                fail++;
408                chown(path, 0, 0);
409           } else if (errno != EPERM) {
410                fprintf(stderr, "chown(%s, 1, 1) did not set errno to EPERM\n", path);
411                fail++;
412           }
413
414           errno = 0;
415           if (del_acl(path) != 1) {
416                if (errno != EOPNOTSUPP) {
417                fprintf(stderr, "del_acl(%s) did not fail\n", path);
418                fail++;
419                }
420           } else if (errno != EPERM) {
421                fprintf(stderr, "del_acl(%s) did not set errno == EPERM\n", path);
422                fail++;
423           }
424           errno = 0;
425           if (add_acl(path, acl_text) != 1) {
426                if (errno != EOPNOTSUPP) {
427                fprintf(stderr, "add_acl(%s) did not fail\n", path);
428                fail++;
429                }
430           } else if (errno != EPERM) {
431                fprintf(stderr, "add_acl(%s) did not set errno == EPERM\n", path);
432                fail++;
433           }
434
435           if (setxattr(path, "trusted.test", "scribble", strlen("scribble"), XATTR_REPLACE) != -1) {
436                fprintf(stderr, "setxattr(%s, trusted.test, scribble, 8, XATTR_REPLACE) did not fail\n", path);
437                fail++;
438           } else if (errno != EPERM && errno != EOPNOTSUPP) {
439                fprintf(stderr,
440                        "setxattr(%s, trusted.test, scribble, 8, XATTR_REPLACE) did not set errno == EPERM\n", path);
441                fail++;
442           }
443           if (setxattr(path, "trusted.scribble", "scribble", strlen("scribble"), XATTR_CREATE) != -1) {
444                fprintf(stderr, "setxattr(%s, trusted.scribble, scribble, 8, XATTR_CREATE) did not fail\n", path);
445                fail++;
446           } else if (errno != EPERM && errno != EOPNOTSUPP) {
447                fprintf(stderr,
448                        "setxattr(%s, trusted.scribble, scribble, 8, XATTR_CREATE) did not set errno == EPERM\n", path);
449                fail++;
450           }
451
452           if (removexattr(path, "trusted.test") != -1) {
453                fprintf(stderr, "removexattr(%s, trusted.test) did not fail\n", path);
454                fail++;
455           } else if (errno != EPERM && errno != EOPNOTSUPP) {
456                fprintf(stderr,
457                        "removexattr(%s, trusted.test) did not set errno == EPERM\n", path);
458                fail++;
459           }
460      }
461
462      if (setxattr(path, "user.test", "scribble", strlen("scribble"), XATTR_REPLACE) != -1) {
463           fprintf(stderr, "setxattr(%s, user.test, scribble, 8, XATTR_REPLACE) did not fail\n", path);
464           fail++;
465      } else if (errno != EPERM && errno != EOPNOTSUPP) {
466           fprintf(stderr, "setxattr(%s, user.test, scribble, 8, XATTR_REPLACE) did not set errno == EPERM\n", path);
467           fail++;
468      }
469      if (setxattr(path, "user.scribble", "scribble", strlen("scribble"), XATTR_CREATE) != -1) {
470           fprintf(stderr, "setxattr(%s, user.scribble, scribble, 8, XATTR_CREATE) did not fail\n", path);
471           fail++;
472      } else if (errno != EPERM && errno != EOPNOTSUPP) {
473           fprintf(stderr, "setxattr(%s, user.scribble, scribble, 8, XATTR_CREATE) did not set errno == EPERM\n", path);
474           fail++;
475      }
476      if (removexattr(path, "user.test") != -1) {
477           fprintf(stderr, "removexattr(%s, user.test) did not fail\n", path);
478           fail++;
479      } else if (errno != EPERM && errno != EOPNOTSUPP) {
480           fprintf(stderr,
481                   "removexattr(%s, user.test) did not set errno == EPERM\n", path);
482           fail++;
483      }
484
485      asprintf(&linkpath, "%s/immutable.f.newname", dir);
486      errno = 0;
487      if (rename(path, linkpath) != -1) {
488           fprintf(stderr, "rename(%s, %s) did not fail\n", path, linkpath);
489           fail++;
490           rename(linkpath, path);
491      } else if (errno != EPERM) {
492           fprintf(stderr, "rename(%s, %s) did not set errno == EPERM\n", path, linkpath);
493           fail++;
494      }
495      free(linkpath);
496
497      if ((fd = open(path, O_RDONLY)) == -1) {
498           fprintf(stderr, "open(%s, O_RDONLY) failed: %s\n", path, strerror(errno));
499           fail++;
500      } else {
501           if (fstat(fd, &st) == -1) {
502                perror("fstat");
503                fail++;
504           } else if (st.st_size) {
505                if ((buf = malloc(st.st_size)) == NULL)
506                     perror("malloc");
507                else {
508                     if (lseek(fd, 0, SEEK_SET) == -1) {
509                          perror("lseek(fd, 0, SEEK_SET) failed");
510                          fail++;
511                     }
512                     if (read(fd, buf, st.st_size) != st.st_size) {
513                          perror("read failed");
514                          fail++;
515                     }
516                     free(buf);
517                }
518           }
519           close(fd);
520      }
521
522      errno = 0;
523      if (unlink(path) != -1) {
524           fprintf(stderr, "unlink(%s) did not fail\n", path);
525           fail++;
526      } else if (errno != EPERM) {
527           fprintf(stderr, "unlink(%s) did not set errno == EPERM\n", path);
528           fail ++;
529      }
530
531      free(path);
532      asprintf(&path, "%s/immutable.d/file", dir);
533      if ((fd = open(path, O_RDWR)) == -1) {
534           fprintf(stderr, "open(%s, O_RDWR) failed: %s\n", path, strerror(errno));
535           fail++;
536      } else {
537           if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) {
538                fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, (int)strlen(scribble), strerror(errno));
539                fail++;
540           }
541           close(fd);
542      }
543      if (!getuid()) {
544           if (chmod(path, 0777) == -1) {
545                fprintf(stderr, "chmod(%s, 0777) failed: %s\n", path, strerror(errno));
546                fail++;
547           } else
548                chmod(path, 0666);
549           if (chown(path, 1, 1) == -1) {
550                fprintf(stderr, "chown(%s, 1, 1) failed: %s\n", path, strerror(errno));
551                fail++;
552           } else
553                chown(path, 0, 0);
554      }
555
556      asprintf(&linkpath, "%s/immutable.d/file.link", dir);
557      errno = 0;
558      if (link(path, linkpath) != -1) {
559           fprintf(stderr, "link(%s, %s) did not fail\n", path, linkpath);
560           fail++;
561           unlink(linkpath);
562      } else if (errno != EACCES && errno != EPERM) {
563           fprintf(stderr, "link(%s, %s) did not set errno == EACCES or EPERM\n", path, linkpath);
564           fail++;
565      }
566      if (symlink(path, linkpath) != -1) {
567           fprintf(stderr, "symlink(%s, %s) did not fail\n", path, linkpath);
568           fail++;
569           unlink(linkpath);
570      } else if (errno != EACCES && errno != EPERM) {
571           fprintf(stderr, "symlink(%s, %s) did not set errno == EACCES or EPERM\n", path, linkpath);
572           fail++;
573      }
574      free(linkpath);
575      asprintf(&linkpath, "%s/immutable.d/file.newname", dir);
576      if (rename(path, linkpath) != -1) {
577           fprintf(stderr, "rename(%s, %s) did not fail\n", path, linkpath);
578           fail++;
579           rename(linkpath, path);
580      } else if (errno != EACCES && errno != EPERM) {
581           fprintf(stderr, "rename(%s, %s) did not set errno == EACCES or EPERM\n", path, linkpath);
582           fail++;
583      }
584      free(linkpath);
585
586      if (unlink(path) != -1) {
587           fprintf(stderr, "unlink(%s) did not fail\n", path);
588           fail++;
589      } else if (errno != EACCES && errno != EPERM) {
590           fprintf(stderr, "unlink(%s) did not set errno == EACCES or EPERM\n", path);
591           fail++;
592      }
593
594      free(path);
595      asprintf(&path, "%s/immutable.d/newfile", dir);
596      errno = 0;
597      if ((fd = open(path, O_RDWR|O_CREAT, 0666)) != -1) {
598           fprintf(stderr, "open(%s, O_RDWR|O_CREAT, 0666) did not fail\n", path);
599           fail++;
600           unlink(path);
601      } else if (errno != EACCES && errno != EPERM) {
602           fprintf(stderr, "open(%s, O_RDWR|O_CREAT, 0666) did not set errno == EACCES or EPERM\n", path);
603           fail++;
604      }
605      if (!getuid()) {
606           if (stat("/dev/null", &st) != -1) {
607                if (mknod(path, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, st.st_rdev) != -1) {
608                     fprintf(stderr, "mknod(%s, S_IFCHR|0666, %lld) did not fail\n", path, (long long int)st.st_rdev);
609                     fail++;
610                     unlink(path);
611                } else if (errno != EACCES && errno != EPERM) {
612                     fprintf(stderr, "mknod(%s, S_IFCHR|0666, %lld) did not set errno == EACCES or EPERM\n", path, (long long int)st.st_rdev);
613                     fail++;
614                }
615           }
616      }
617
618      free(path);
619      asprintf(&path, "%s/immutable.d/newdir", dir);
620      errno = 0;
621      if (mkdir(path, 0777) != -1) {
622           fprintf(stderr, "mkdir(%s, 0777) did not fail\n", path);
623           fail++;
624           rmdir(path);
625      } else if (errno != EACCES && errno != EPERM) {
626           fprintf(stderr, "mkdir(%s, 0777) did not set errno == EACCES or EPERM\n", path);
627           fail++;
628      }
629
630      free(path);
631      asprintf(&path, "%s/immutable.d/dir/newfile-%d", dir, getuid());
632      if ((fd = open(path, O_RDWR|O_CREAT, 0666)) == -1) {
633           fprintf(stderr, "open(%s, O_RDWR|O_CREAT, 0666) failed: %s\n", path, strerror(errno));
634           fail++;
635      } else {
636           if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) {
637                fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, (int)strlen(scribble), strerror(errno));
638                fail++;
639           }
640           close(fd);
641      }
642      if (!getuid()) {
643           if (chmod(path, 0700) == -1) {
644                fprintf(stderr, "chmod(%s, 0700) failed: %s\n", path, strerror(errno));
645                fail++;
646           } else
647                chmod(path, 0666);
648
649           if (chown(path, 1, 1) == -1) {
650                fprintf(stderr, "chown(%s, 1, 1) failed: %s\n", path, strerror(errno));
651                fail++;
652           } else
653                chown(path, 0, 0);
654      }
655
656      free(path);
657      asprintf(&path, "%s/immutable.d/dir", dir);
658      errno = 0;
659      if (rmdir(path) != -1) {
660           fprintf(stderr, "rmdir(%s) did not fail\n", path);
661           fail++;
662      } else if (errno != EACCES && errno != EPERM) {
663           fprintf(stderr, "rmdir(%s) did not set errno == EACCES or EPERM\n", path);
664           fail++;
665      }
666
667      free(path);
668      asprintf(&path, "%s/immutable.d", dir);
669
670 #ifdef TEST_UTIME
671      errno = 0;
672      if (utime(path, &tbuf) != -1) {
673           fprintf(stderr, "utime(%s, <epoch>) did not fail\n", path);
674           fail++;
675      } else if (errno != EPERM) {
676           fprintf(stderr, "utime(%s, <epoch>) did not set errno == EPERM\n", path);
677           fail++;
678      }
679
680      errno = 0;
681      if (utime(path, NULL) != -1) {
682           fprintf(stderr, "utime(%s, NULL) did not fail\n", path);
683           fail++;
684      } else if (errno != EACCES && errno != EPERM) {
685           fprintf(stderr, "utime(%s, NULL) did not set errno == EACCES or EPERM\n", path);
686           fail++;
687      }
688 #endif /* TEST_UTIME */
689
690      if (!getuid()) { /* these would fail if not root anyway */
691           errno = 0;
692           if (chmod(path, 7777) != -1) {
693                fprintf(stderr, "chmod(%s, 7777) did not fail\n", path);
694                fail++;
695                chmod(path, 0666);
696           } else if (errno != EPERM) {
697                fprintf(stderr, "chmod(%s, 7777) did not set errno == EPERM\n", path);
698                fail++;
699           }
700
701           errno = 0;
702           if (chown(path, 1, 1) != -1) {
703                fprintf(stderr, "chown(%s, 1, 1) did not fail\n", path);
704                fail++;
705                chown(path, 0, 0);
706           } else if (errno != EPERM) {
707                fprintf(stderr, "chown(%s, 1, 1) did not set errno to EPERM\n", path);
708                fail++;
709           }
710
711           errno = 0;
712           if (del_acl(path) != 1) {
713                if (errno != EOPNOTSUPP) {
714                fprintf(stderr, "del_acl(%s) did not fail\n", path);
715                fail++;
716                }
717           } else if (errno != EPERM) {
718                fprintf(stderr, "del_acl(%s) did not set errno == EPERM\n", path);
719                fail++;
720           }
721           errno = 0;
722           if (add_acl(path, acl_text) != 1) {
723                if (errno != EOPNOTSUPP) {
724                fprintf(stderr, "add_acl(%s) did not fail\n", path);
725                fail++;
726                }
727           } else if (errno != EPERM) {
728                fprintf(stderr, "add_acl(%s) did not set errno == EPERM\n", path);
729                fail++;
730           }
731
732           if (setxattr(path, "trusted.test", "scribble", strlen("scribble"), XATTR_REPLACE) != -1) {
733                fprintf(stderr, "setxattr(%s, trusted.test, scribble, 8, XATTR_REPLACE) did not fail\n", path);
734                fail++;
735           } else if (errno != EPERM && errno != EOPNOTSUPP) {
736                fprintf(stderr,
737                        "setxattr(%s, trusted.test, scribble, 8, XATTR_REPLACE) did not set errno == EPERM\n", path);
738                fail++;
739           }
740           if (setxattr(path, "trusted.scribble", "scribble", strlen("scribble"), XATTR_CREATE) != -1) {
741                fprintf(stderr, "setxattr(%s, trusted.scribble, scribble, 8, XATTR_CREATE) did not fail\n", path);
742                fail++;
743           } else if (errno != EPERM && errno != EOPNOTSUPP) {
744                fprintf(stderr,
745                        "setxattr(%s, trusted.scribble, scribble, 8, XATTR_CREATE) did not set errno == EPERM\n", path);
746                fail++;
747           }
748           if (removexattr(path, "trusted.test") != -1) {
749                fprintf(stderr, "removexattr(%s, trusted.test) did not fail\n", path);
750                fail++;
751           } else if (errno != EPERM && errno != EOPNOTSUPP) {
752                fprintf(stderr,
753                        "removexattr(%s, trusted.test) did not set errno == EPERM\n", path);
754                fail++;
755           }
756      }
757
758      if (setxattr(path, "user.test", "scribble", strlen("scribble"), XATTR_REPLACE) != -1) {
759           fprintf(stderr, "setxattr(%s, user.test, scribble, 8, XATTR_REPLACE) did not fail\n", path);
760           fail++;
761      } else if (errno != EPERM && errno != EOPNOTSUPP) {
762           fprintf(stderr, "setxattr(%s, user.test, scribble, 8, XATTR_REPLACE) did not set errno == EPERM\n", path);
763           fail++;
764      }
765      if (setxattr(path, "user.scribble", "scribble", strlen("scribble"), XATTR_CREATE) != -1) {
766           fprintf(stderr, "setxattr(%s, user.scribble, scribble, 8, XATTR_CREATE) did not fail\n", path);
767           fail++;
768      } else if (errno != EPERM && errno != EOPNOTSUPP) {
769           fprintf(stderr, "setxattr(%s, user.scribble, scribble, 8, XATTR_CREATE) did not set errno == EPERM\n", path);
770           fail++;
771      }
772      if (removexattr(path, "user.test") != -1) {
773           fprintf(stderr, "removexattr(%s, user.test) did not fail\n", path);
774           fail++;
775      } else if (errno != EPERM && errno != EOPNOTSUPP) {
776           fprintf(stderr,
777                   "removexattr(%s, user.test) did not set errno == EPERM\n", path);
778           fail++;
779      }
780
781      free(path);
782      asprintf(&path, "%s/empty-immutable.d", dir);
783      errno = 0;
784      if (rmdir(path) != -1) {
785           fprintf(stderr, "rmdir(%s) did not fail\n", path);
786           fail++;
787      } else if (errno != EPERM) {
788           fprintf(stderr, "rmdir(%s) did not set errno == EPERM\n", path);
789           fail++;
790      }
791
792      free(path);
793      return fail;
794 }
795
796 static int test_append(const char *dir)
797 {
798      int fd;
799      char *buf;
800      char *orig = NULL;
801      char *path;
802      char *linkpath;
803      off_t origsize = 0;
804      int fail = 0;
805      struct utimbuf tbuf;
806      struct stat st;
807      struct statfs stfs;
808      static const char *acl_text = "u::rwx,g::rwx,o::rwx,u:daemon:rwx,m::rwx";
809      static const char *scribble = "scribbled by tester\n";
810      static const char *scribble2 = "scribbled by tester\nscribbled by tester\n";
811      static const char *scribble4 = "scribbled by tester\nscribbled by tester\n"
812           "scribbled by tester\nscribbled by tester\n";
813
814      tbuf.actime = 0;
815      tbuf.modtime = 0;
816
817      if (statfs(dir, &stfs) == -1) {
818           perror("statfs failed");
819           return 1;
820      }
821
822      asprintf(&path, "%s/append-only.f", dir);
823      errno = 0;
824      if ((fd = open(path, O_RDWR)) != -1) {
825           fprintf(stderr, "open(%s, O_RDWR) did not fail\n", path);
826           fail++;
827           close(fd);
828      } else if (errno != EPERM) {
829           fprintf(stderr, "open(%s, O_RDWR) did not set errno == EPERM\n", path);
830           fail++;
831      }
832
833      errno = 0;
834      if ((fd = open(path, O_WRONLY)) != -1) {
835           fprintf(stderr, "open(%s, O_WRONLY) did not fail\n", path);
836           fail++;
837           close(fd);
838      } else if (errno != EPERM) {
839           fprintf(stderr, "open(%s, O_WRONLY) did not set errno == EPERM\n", path);
840           fail++;
841      }
842
843      errno = 0;
844      if ((fd = open(path, O_RDWR|O_TRUNC)) != -1) {
845           fprintf(stderr, "open(%s, O_RDWR|O_TRUNC) did not fail\n", path);
846           fail++;
847           close(fd);
848      } else if (errno != EPERM) {
849           fprintf(stderr, "open(%s, O_RDWR|O_TRUNC) did not set errno == EPERM\n", path);
850           fail++;
851      }
852
853      errno = 0;
854      if ((fd = open(path, O_WRONLY|O_TRUNC)) != -1) {
855           fprintf(stderr, "open(%s, O_WRONLY|O_TRUNC) did not fail\n", path);
856           fail++;
857           close(fd);
858      } else if (errno != EPERM) {
859           fprintf(stderr, "open(%s, O_WRONLY|O_TRUNC did not set errno == EPERM\n", path);
860           fail++;
861      }
862
863      errno = 0;
864      if ((fd = open(path, O_RDWR|O_APPEND|O_TRUNC)) != -1) {
865           fprintf(stderr, "open(%s, O_RDWR|O_APPEND|O_TRUNC) did not fail\n", path);
866           fail++;
867           close(fd);
868      } else if (errno != EPERM) {
869           fprintf(stderr, "open(%s, O_RDWR|O_APPEND|O_TRUNC) did not set errno == EPERM\n", path);
870           fail++;
871      }
872
873      errno = 0;
874      if ((fd = open(path, O_WRONLY|O_APPEND|O_TRUNC)) != -1) {
875           fprintf(stderr, "open(%s, O_WRONLY|O_APPEND|O_TRUNC) did not fail\n", path);
876           fail++;
877           close(fd);
878      } else if (errno != EPERM) {
879           fprintf(stderr, "open(%s, O_WRONLY|O_APPEND|O_TRUNC) did not set errno == EPERM\n", path);
880           fail++;
881      }
882
883      errno = 0;
884      if ((fd = open(path, O_RDWR|O_APPEND)) == -1) {
885           fprintf(stderr, "open(%s, O_RDWR|O_APPEND) failed: %s\n", path, strerror(errno));
886           fail++;
887      } else {
888              if (ftruncate(fd, 0) != -1) {
889                      fprintf(stderr, "ftruncate(%s, 0) did not fail\n", path);
890                      fail++;
891              } else if (errno != EPERM) {
892                      fprintf(stderr, "ftruncate(%s, 0) did not set errno == EPERM\n", path);
893                      fail++;
894              }
895              close(fd);
896      }
897
898      errno = 0;
899      if (truncate(path, 0) != -1) {
900           fprintf(stderr, "truncate(%s, 0) did not fail\n", path);
901           fail++;
902      } else if (errno != EPERM) {
903           fprintf(stderr, "truncate(%s, 0) did not set errno == EPERM\n", path);
904           fail++;
905      }
906
907      if (stfs.f_type == XFS_SUPER_MAGIC && !getuid()) {
908           jdm_fshandle_t *fshandle;
909           struct xfs_bstat bstat;
910           struct xfs_fsop_bulkreq  bulkreq;
911           xfs_ino_t ino;
912           char *dirpath;
913
914           dirpath = strdup(path); /* dirname obnoxiously modifies its arg */
915           if ((fshandle = jdm_getfshandle(dirname(dirpath))) == NULL) {
916                perror("jdm_getfshandle");
917                return 1;
918           }
919           free(dirpath);
920
921           if (stat(path, &st) != 0) {
922                perror("stat");
923                return 1;
924           }
925
926           ino = st.st_ino;
927
928           bulkreq.lastip = (__u64 *)&ino;
929           bulkreq.icount = 1;
930           bulkreq.ubuffer = &bstat;
931           bulkreq.ocount = NULL;
932
933           if ((fd = open(path, O_RDONLY)) == -1) {
934                perror("open");
935                return 1;
936           }
937
938           if (ioctl(fd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq) == -1) {
939                perror("ioctl(fd, XFS_IOC_FSBULKSTAT_SINGLE");
940                close(fd);
941                return 1;
942           }
943           close(fd);
944
945           errno = 0;
946           if ((fd = jdm_open(fshandle, &bstat, O_RDWR)) != -1) {
947                fprintf(stderr, "jdm_open(%s, O_RDWR) did not fail\n", path);
948                fail++;
949                close(fd);
950           } else if (errno != EPERM) {
951                perror("jdm_open");
952                fprintf(stderr, "jdm_open(%s, O_RDWR) did not set errno == EPERM\n", path);
953                fail++;
954           }
955
956           errno = 0;
957           if ((fd = jdm_open(fshandle, &bstat, O_WRONLY)) != -1) {
958                fprintf(stderr, "jdm_open(%s, O_WRONLY) did not fail\n", path);
959                fail++;
960                close(fd);
961           } else if (errno != EPERM) {
962                fprintf(stderr, "jdm_open(%s, O_WRONLY) did not set errno == EPERM\n", path);
963                fail++;
964           }
965
966           errno = 0;
967           if ((fd = jdm_open(fshandle, &bstat, O_RDWR|O_TRUNC)) != -1) {
968                fprintf(stderr, "jdm_open(%s, O_RDWR|O_TRUNC) did not fail\n", path);
969                fail++;
970                close(fd);
971           } else if (errno != EPERM) {
972                fprintf(stderr, "jdm_open(%s, O_RDWR|O_TRUNC) did not set errno == EPERM\n", path);
973                fail++;
974           }
975
976           errno = 0;
977           if ((fd = jdm_open(fshandle, &bstat, O_WRONLY|O_TRUNC)) != -1) {
978                fprintf(stderr, "jdm_open(%s, O_WRONLY|O_TRUNC) did not fail\n", path);
979                fail++;
980                close(fd);
981           } else if (errno != EPERM) {
982                fprintf(stderr, "jdm_open(%s, O_WRONLY|O_TRUNC did not set errno == EPERM\n", path);
983                fail++;
984           }
985
986           errno = 0;
987           if ((fd = jdm_open(fshandle, &bstat, O_RDWR|O_APPEND|O_TRUNC)) != -1) {
988                fprintf(stderr, "jdm_open(%s, O_RDWR|O_APPEND|O_TRUNC) did not fail\n", path);
989                fail++;
990                close(fd);
991           } else if (errno != EPERM) {
992                fprintf(stderr, "jdm_open(%s, O_RDWR|O_APPEND|O_TRUNC) did not set errno == EPERM\n", path);
993                fail++;
994           }
995
996           errno = 0;
997           if ((fd = jdm_open(fshandle, &bstat, O_WRONLY|O_APPEND|O_TRUNC)) != -1) {
998                fprintf(stderr, "jdm_open(%s, O_WRONLY|O_APPEND|O_TRUNC) did not fail\n", path);
999                fail++;
1000                close(fd);
1001           } else if (errno != EPERM) {
1002                fprintf(stderr, "jdm_open(%s, O_WRONLY|O_APPEND|O_TRUNC) did not set errno == EPERM\n", path);
1003                fail++;
1004           }
1005      }
1006
1007 #ifdef TEST_UTIME
1008      errno = 0;
1009      if (utime(path, &tbuf) != -1) {
1010           fprintf(stderr, "utime(%s, <epoch>) did not fail\n", path);
1011           fail++;
1012      } else if (errno != EPERM) {
1013           fprintf(stderr, "utime(%s, <epoch>) did not set errno == EPERM\n", path);
1014           fail++;
1015      }
1016
1017      errno = 0;
1018      if (utime(path, NULL) == -1) {
1019           fprintf(stderr, "utime(%s, NULL) failed\n", path);
1020           fail++;
1021      }
1022 #endif /* TEST_UTIME */
1023
1024      asprintf(&linkpath, "%s/append-only.f.hardlink", dir);
1025      errno = 0;
1026      if (link(path, linkpath) != -1) {
1027           fprintf(stderr, "link(%s, %s) did not fail\n", path, linkpath);
1028           fail++;
1029           unlink(linkpath);
1030      } else if (errno != EPERM) {
1031           fprintf(stderr, "link(%s, %s) did not set errno == EPERM\n", path, linkpath);
1032           fail++;
1033      }
1034      free(linkpath);
1035
1036      if (!getuid()) { /* these would fail if not root anyway */
1037           errno = 0;
1038           if (chmod(path, 7777) != -1) {
1039                fprintf(stderr, "chmod(%s, 7777) did not fail\n", path);
1040                fail++;
1041                chmod(path, 0666);
1042           } else if (errno != EPERM) {
1043                fprintf(stderr, "chmod(%s, 7777) did not set errno == EPERM\n", path);
1044                fail++;
1045           }
1046
1047           errno = 0;
1048           if (chown(path, 1, 1) != -1) {
1049                fprintf(stderr, "chown(%s, 1, 1) did not fail\n", path);
1050                fail++;
1051                chown(path, 0, 0);
1052           } else if (errno != EPERM) {
1053                fprintf(stderr, "chown(%s, 1, 1) did not set errno to EPERM\n", path);
1054                fail++;
1055           }
1056           errno = 0;
1057           if (del_acl(path) != 1) {
1058                if (errno != EOPNOTSUPP) {
1059                fprintf(stderr, "del_acl(%s) did not fail\n", path);
1060                fail++;
1061                }
1062           } else if (errno != EPERM) {
1063                fprintf(stderr, "del_acl(%s) did not set errno == EPERM\n", path);
1064                fail++;
1065           }
1066           errno = 0;
1067           if (add_acl(path, acl_text) != 1) {
1068                if (errno != EOPNOTSUPP) {
1069                fprintf(stderr, "add_acl(%s) did not fail\n", path);
1070                fail++;
1071                }
1072           } else if (errno != EPERM) {
1073                fprintf(stderr, "add_acl(%s) did not set errno == EPERM\n", path);
1074                fail++;
1075           }
1076
1077           if (setxattr(path, "trusted.test", "scribble", strlen("scribble"), XATTR_REPLACE) != -1) {
1078                fprintf(stderr, "setxattr(%s, trusted.test, scribble, 8, XATTR_REPLACE) did not fail\n", path);
1079                fail++;
1080           } else if (errno != EPERM && errno != EOPNOTSUPP) {
1081                fprintf(stderr,
1082                        "setxattr(%s, trusted.test, scribble, 8, XATTR_REPLACE) did not set errno == EPERM\n", path);
1083                fail++;
1084           }
1085           if (setxattr(path, "trusted.scribble", "scribble", strlen("scribble"), XATTR_CREATE) != -1) {
1086                fprintf(stderr, "setxattr(%s, trusted.scribble, scribble, 8, XATTR_CREATE) did not fail\n", path);
1087                fail++;
1088           } else if (errno != EPERM && errno != EOPNOTSUPP) {
1089                fprintf(stderr,
1090                        "setxattr(%s, trusted.scribble, scribble, 8, XATTR_CREATE) did not set errno == EPERM\n", path);
1091                fail++;
1092           }
1093           if (removexattr(path, "trusted.test") != -1) {
1094                fprintf(stderr, "removexattr(%s, trusted.test) did not fail\n", path);
1095                fail++;
1096           } else if (errno != EPERM && errno != EOPNOTSUPP) {
1097                fprintf(stderr,
1098                        "removexattr(%s, trusted.test) did not set errno == EPERM\n", path);
1099                fail++;
1100           }
1101      }
1102
1103      if (setxattr(path, "user.test", "scribble", strlen("scribble"), XATTR_REPLACE) != -1) {
1104           fprintf(stderr, "setxattr(%s, user.test, scribble, 8, XATTR_REPLACE) did not fail\n", path);
1105           fail++;
1106      } else if (errno != EPERM && errno != EOPNOTSUPP) {
1107           fprintf(stderr, "setxattr(%s, user.test, scribble, 8, XATTR_REPLACE) did not set errno == EPERM\n", path);
1108           fail++;
1109      }
1110      if (setxattr(path, "user.scribble", "scribble", strlen("scribble"), XATTR_CREATE) != -1) {
1111           fprintf(stderr, "setxattr(%s, user.scribble, scribble, 8, XATTR_CREATE) did not fail\n", path);
1112           fail++;
1113      } else if (errno != EPERM && errno != EOPNOTSUPP) {
1114           fprintf(stderr, "setxattr(%s, user.scribble, scribble, 8, XATTR_CREATE) did not set errno == EPERM\n", path);
1115           fail++;
1116      }
1117      if (removexattr(path, "user.test") != -1) {
1118           fprintf(stderr, "removexattr(%s, user.test) did not fail\n", path);
1119           fail++;
1120      } else if (errno != EPERM && errno != EOPNOTSUPP) {
1121           fprintf(stderr,
1122                   "removexattr(%s, user.test) did not set errno == EPERM\n", path);
1123           fail++;
1124      }
1125
1126      asprintf(&linkpath, "%s/append-only.f.newname", dir);
1127      errno = 0;
1128      if (rename(path, linkpath) != -1) {
1129           fprintf(stderr, "rename(%s, %s) did not fail\n", path, linkpath);
1130           fail++;
1131           rename(linkpath, path);
1132      } else if (errno != EPERM) {
1133           fprintf(stderr, "rename(%s, %s) did not set errno == EPERM\n", path, linkpath);
1134           fail++;
1135      }
1136      free(linkpath);
1137
1138      if ((fd = open(path, O_RDONLY)) == -1) {
1139           fprintf(stderr, "open(%s, O_RDONLY) failed: %s\n", path, strerror(errno));
1140           fail++;
1141      } else {
1142           if (fstat(fd, &st) == -1) {
1143                perror("fstat");
1144                fail++;
1145           } else if (st.st_size) {
1146                origsize = st.st_size;
1147                if ((orig = malloc(st.st_size)) == NULL)
1148                     perror("malloc");
1149                else {
1150                     if (lseek(fd, 0, SEEK_SET) == -1) {
1151                          perror("lseek(fd, 0, SEEK_SET) failed");
1152                          fail++;
1153                     }
1154                     if (read(fd, orig, st.st_size) != st.st_size) {
1155                          perror("read failed");
1156                          fail++;
1157                     }
1158                }
1159           }
1160           close(fd);
1161      }
1162
1163      if ((fd = open(path, O_WRONLY|O_APPEND)) == -1) {
1164           fprintf(stderr, "open(%s, O_WRONLY|O_APPEND) failed: %s\n", path, strerror(errno));
1165           fail++;
1166      } else {
1167           lseek(fd, 0, SEEK_SET); /* this is silently ignored */
1168           if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) {
1169                fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, (int)strlen(scribble),
1170                        strerror(errno));
1171                fail++;
1172           }
1173           lseek(fd, origsize, SEEK_SET); /* this is silently ignored */
1174           if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) {
1175                fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, (int)strlen(scribble),
1176                        strerror(errno));
1177                fail++;
1178           }
1179           close(fd);
1180           if ((fd = open(path, O_RDONLY)) == -1) { /* now we check to make sure lseek() ignored us */
1181                fprintf(stderr, "open(%s, O_RDONLY) failed: %s\n", path, strerror(errno));
1182                fail++;
1183           } else {
1184                if (lseek(fd, 0, SEEK_SET) == -1) {
1185                     fprintf(stderr, "lseek(%s, 0, SEEK_SET) failed: %s\n", path, strerror(errno));
1186                     fail++;
1187                } else if (origsize) {
1188                     if ((buf = malloc(origsize)) == NULL)
1189                          perror("malloc");
1190                     else {
1191                          if (read(fd, buf, origsize) == -1) {
1192                               fprintf(stderr, "read(%s, &buf, %ld) failed: %s\n", path, (long)origsize, strerror(errno));
1193                               fail++;
1194                          } else {
1195                               if (memcmp(orig, buf, origsize)) {
1196                                    fprintf(stderr, "existing data in append-only.f was overwritten\n");
1197                                    fail++;
1198                               }
1199                          }
1200                          free(buf);
1201                     }
1202                }
1203                if (lseek(fd, origsize, SEEK_SET) == -1) {
1204                     fprintf(stderr, "lseek(%s, %ld, SEEK_SET) failed: %s\n", path, (long)origsize, strerror(errno));
1205                     fail++;
1206                } else {
1207                     if ((buf = malloc(strlen(scribble2))) == NULL)
1208                          perror("malloc");
1209                     else {
1210                          if (read(fd, buf, strlen(scribble2)) == -1) {
1211                               fprintf(stderr, "read(%s, &buf, %d) failed: %s\n", path,
1212                                       (int)strlen(scribble2), strerror(errno));
1213                               fail++;
1214                          } else {
1215                               if (memcmp(scribble2, buf, strlen(scribble2))) {
1216                                    fprintf(stderr, "existing data in append-only.f was overwritten\n");
1217                                    fail++;
1218                               }
1219                          }
1220                          free(buf);
1221                     }
1222                     close(fd);
1223                }
1224           }
1225      }
1226
1227      if ((fd = open(path, O_RDWR|O_APPEND)) == -1) {
1228           fprintf(stderr, "open(%s, O_RDWR|O_APPEND) failed: %s\n", path, strerror(errno));
1229           fail++;
1230      } else {
1231           lseek(fd, 0, SEEK_SET); /* this is silently ignored */
1232           if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) {
1233                fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, (int)strlen(scribble),
1234                        strerror(errno));
1235                fail++;
1236           }
1237           lseek(fd, origsize, SEEK_SET); /* this is silently ignored */
1238           if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) {
1239                fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, (int)strlen(scribble),
1240                        strerror(errno));
1241                fail++;
1242           }
1243           close(fd);
1244           if ((fd = open(path, O_RDONLY)) == -1) { /* now we check to make sure lseek() ignored us */
1245                fprintf(stderr, "open(%s, O_RDONLY) failed: %s\n", path, strerror(errno));
1246                fail++;
1247           } else {
1248                if (lseek(fd, 0, SEEK_SET) == -1) {
1249                     fprintf(stderr, "lseek(%s, 0, SEEK_SET) failed: %s\n", path, strerror(errno));
1250                     fail++;
1251                } else if (origsize) {
1252                     if ((buf = malloc(origsize)) == NULL)
1253                          perror("malloc");
1254                     else {
1255                          if (read(fd, buf, origsize) == -1) {
1256                               fprintf(stderr, "read(%s, &buf, %ld) failed: %s\n", path, (long)origsize, strerror(errno));
1257                               fail++;
1258                          } else {
1259                               if (memcmp(orig, buf, origsize)) {
1260                                    fprintf(stderr, "existing data in append-only.f was overwritten\n");
1261                                    fail++;
1262                               }
1263                          }
1264                          free(buf);
1265                          free(orig);
1266                     }
1267                }
1268                if (lseek(fd, origsize, SEEK_SET) == -1) {
1269                     fprintf(stderr, "lseek(%s, %ld, SEEK_SET) failed: %s\n", path, (long)origsize, strerror(errno));
1270                     fail++;
1271                } else if (scribble4) {
1272                     if ((buf = malloc(strlen(scribble4))) == NULL)
1273                          perror("malloc");
1274                     else {
1275                          if (read(fd, buf, strlen(scribble4)) == -1) {
1276                               fprintf(stderr, "read(%s, &buf, %d) failed: %s\n", path,
1277                                       (int)strlen(scribble4), strerror(errno));
1278                               fail++;
1279                          } else {
1280                               if (memcmp(scribble4, buf, strlen(scribble4))) {
1281                                    fprintf(stderr, "existing data in append-only.f was overwritten\n");
1282                                    fail++;
1283                               }
1284                          }
1285                          free(buf);
1286                     }
1287                     close(fd);
1288                }
1289           }
1290      }
1291
1292      if (stfs.f_type == XFS_SUPER_MAGIC && !getuid()) {
1293           jdm_fshandle_t *fshandle;
1294           struct xfs_bstat bstat;
1295           struct xfs_fsop_bulkreq  bulkreq;
1296           xfs_ino_t ino;
1297           char *dirpath;
1298
1299           if ((fd = open(path, O_RDONLY)) == -1) {
1300                fprintf(stderr, "open(%s, O_RDONLY) failed: %s\n", path, strerror(errno));
1301                fail++;
1302           } else {
1303                if (fstat(fd, &st) == -1) {
1304                     perror("fstat");
1305                     fail++;
1306                } else if (st.st_size) {
1307                     origsize = st.st_size;
1308                     if ((orig = malloc(st.st_size)) == NULL)
1309                          perror("malloc");
1310                     else {
1311                          if (lseek(fd, 0, SEEK_SET) == -1) {
1312                               perror("lseek(fd, 0, SEEK_SET) failed");
1313                               fail++;
1314                          }
1315                          if (read(fd, orig, st.st_size) != st.st_size) {
1316                               perror("read failed");
1317                               fail++;
1318                          }
1319                     }
1320                }
1321                close(fd);
1322           }
1323
1324           dirpath = strdup(path); /* dirname obnoxiously modifies its arg */
1325           if ((fshandle = jdm_getfshandle(dirname(dirpath))) == NULL) {
1326                perror("jdm_getfshandle");
1327                return 1;
1328           }
1329           free(dirpath);
1330
1331           if (stat(path, &st) != 0) {
1332                perror("stat");
1333                return 1;
1334           }
1335
1336           ino = st.st_ino;
1337
1338           bulkreq.lastip = (__u64 *)&ino;
1339           bulkreq.icount = 1;
1340           bulkreq.ubuffer = &bstat;
1341           bulkreq.ocount = NULL;
1342
1343           if ((fd = open(path, O_RDONLY)) == -1) {
1344                perror("open");
1345                return 1;
1346           }
1347
1348           if (ioctl(fd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq) == -1) {
1349                perror("ioctl(fd, XFS_IOC_FSBULKSTAT_SINGLE");
1350                close(fd);
1351                return 1;
1352           }
1353           close(fd);
1354
1355           if ((fd = jdm_open(fshandle, &bstat, O_WRONLY|O_APPEND)) == -1) {
1356                fprintf(stderr, "jdm_open(%s, O_WRONLY|O_APPEND) failed: %s\n", path, strerror(errno));
1357                fail++;
1358           } else {
1359                lseek(fd, 0, SEEK_SET); /* this is silently ignored */
1360                if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) {
1361                     fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, (int)strlen(scribble),
1362                             strerror(errno));
1363                     fail++;
1364                }
1365                lseek(fd, origsize, SEEK_SET); /* this is silently ignored */
1366                if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) {
1367                     fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, (int)strlen(scribble),
1368                             strerror(errno));
1369                     fail++;
1370                }
1371                close(fd);
1372                if ((fd = jdm_open(fshandle, &bstat, O_RDONLY)) == -1) { /* now we check to make sure lseek() ignored us */
1373                     fprintf(stderr, "jdm_open(%s, O_RDONLY) failed: %s\n", path, strerror(errno));
1374                     fail++;
1375                } else {
1376                     if (lseek(fd, 0, SEEK_SET) == -1) {
1377                          fprintf(stderr, "lseek(%s, 0, SEEK_SET) failed: %s\n", path, strerror(errno));
1378                          fail++;
1379                     } else if (origsize) {
1380                          if ((buf = malloc(origsize)) == NULL)
1381                               perror("malloc");
1382                          else {
1383                               if (read(fd, buf, origsize) == -1) {
1384                                    fprintf(stderr, "read(%s, &buf, %ld) failed: %s\n", path, (long)origsize, strerror(errno));
1385                                    fail++;
1386                               } else {
1387                                    if (memcmp(orig, buf, origsize)) {
1388                                         fprintf(stderr, "existing data in append-only.f was overwritten\n");
1389                                         fail++;
1390                                    }
1391                               }
1392                               free(buf);
1393                          }
1394                     }
1395                     if (lseek(fd, origsize, SEEK_SET) == -1) {
1396                          fprintf(stderr, "lseek(%s, %ld, SEEK_SET) failed: %s\n", path, (long)origsize, strerror(errno));
1397                          fail++;
1398                     } else if (strlen(scribble2)) {
1399                          if ((buf = malloc(strlen(scribble2))) == NULL)
1400                               perror("malloc");
1401                          else {
1402                               if (read(fd, buf, strlen(scribble2)) == -1) {
1403                                    fprintf(stderr, "read(%s, &buf, %d) failed: %s\n", path,
1404                                            (int)strlen(scribble2), strerror(errno));
1405                                    fail++;
1406                               } else {
1407                                    if (memcmp(scribble2, buf, strlen(scribble2))) {
1408                                         fprintf(stderr, "existing data in append-only.f was overwritten\n");
1409                                         fail++;
1410                                    }
1411                               }
1412                               free(buf);
1413                          }
1414                          close(fd);
1415                     }
1416                }
1417           }
1418
1419           if ((fd = jdm_open(fshandle, &bstat, O_RDWR|O_APPEND)) == -1) {
1420                fprintf(stderr, "jdm_open(%s, O_RDWR|O_APPEND) failed: %s\n", path, strerror(errno));
1421                fail++;
1422           } else {
1423                lseek(fd, 0, SEEK_SET); /* this is silently ignored */
1424                if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) {
1425                     fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, (int)strlen(scribble),
1426                             strerror(errno));
1427                     fail++;
1428                }
1429                lseek(fd, origsize, SEEK_SET); /* this is silently ignored */
1430                if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) {
1431                     fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, (int)strlen(scribble),
1432                             strerror(errno));
1433                     fail++;
1434                }
1435                close(fd);
1436                if ((fd = jdm_open(fshandle, &bstat, O_RDONLY)) == -1) { /* now we check to make sure lseek() ignored us */
1437                     fprintf(stderr, "jdm_open(%s, O_RDONLY) failed: %s\n", path, strerror(errno));
1438                     fail++;
1439                } else {
1440                     if (lseek(fd, 0, SEEK_SET) == -1) {
1441                          fprintf(stderr, "lseek(%s, 0, SEEK_SET) failed: %s\n", path, strerror(errno));
1442                          fail++;
1443                     } else if (origsize) {
1444                          if ((buf = malloc(origsize)) == NULL)
1445                               perror("malloc");
1446                          else {
1447                               if (read(fd, buf, origsize) == -1) {
1448                                    fprintf(stderr, "read(%s, &buf, %ld) failed: %s\n", path, (long)origsize, strerror(errno));
1449                                    fail++;
1450                               } else {
1451                                    if (memcmp(orig, buf, origsize)) {
1452                                         fprintf(stderr, "existing data in append-only.f was overwritten\n");
1453                                         fail++;
1454                                    }
1455                               }
1456                               free(buf);
1457                               free(orig);
1458                          }
1459                     }
1460                     if (lseek(fd, origsize, SEEK_SET) == -1) {
1461                          fprintf(stderr, "lseek(%s, %ld, SEEK_SET) failed: %s\n", path, (long)origsize, strerror(errno));
1462                          fail++;
1463                     } else if (strlen(scribble4)) {
1464                          if ((buf = malloc(strlen(scribble4))) == NULL)
1465                               perror("malloc");
1466                          else {
1467                               if (read(fd, buf, strlen(scribble4)) == -1) {
1468                                    fprintf(stderr, "read(%s, &buf, %d) failed: %s\n", path,
1469                                            (int)strlen(scribble4), strerror(errno));
1470                                    fail++;
1471                               } else {
1472                                    if (memcmp(scribble4, buf, strlen(scribble4))) {
1473                                         fprintf(stderr, "existing data in append-only.f was overwritten\n");
1474                                         fail++;
1475                                    }
1476                               }
1477                               free(buf);
1478                          }
1479                          close(fd);
1480                     }
1481                }
1482           }
1483      }
1484
1485      errno = 0;
1486      if (unlink(path) != -1) {
1487           fprintf(stderr, "unlink(%s) did not fail\n", path);
1488           fail++;
1489      } else if (errno != EPERM) {
1490           fprintf(stderr, "unlink(%s) did not set errno == EPERM\n", path);
1491           fail ++;
1492      }
1493
1494      free(path);
1495      asprintf(&path, "%s/append-only.d/file", dir);
1496      if ((fd = open(path, O_RDWR)) == -1) {
1497           fprintf(stderr, "open(%s, O_RDWR) failed: %s\n", path, strerror(errno));
1498           fail++;
1499      } else {
1500           if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) {
1501                fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, (int)strlen(scribble), strerror(errno));
1502                fail++;
1503           }
1504           close(fd);
1505      }
1506      if (!getuid()) {
1507           if (chmod(path, 0777) == -1) {
1508                fprintf(stderr, "chmod(%s, 0777) failed: %s\n", path, strerror(errno));
1509                fail++;
1510           } else
1511                chmod(path, 0666);
1512           if (chown(path, 1, 1) == -1) {
1513                fprintf(stderr, "chown(%s, 1, 1) failed: %s\n", path, strerror(errno));
1514                fail++;
1515           } else
1516                chown(path, 0, 0);
1517      }
1518
1519      asprintf(&linkpath, "%s/append-only.d/file.link-%d", dir, getuid());
1520      errno = 0;
1521      if (link(path, linkpath) == -1) {
1522           fprintf(stderr, "link(%s, %s) failed: %s\n", path, linkpath, strerror(errno));
1523           fail++;
1524      } else if (unlink(linkpath) != -1) {
1525           fprintf(stderr, "unlink(%s) did not fail\n", linkpath);
1526           fail++;
1527      }
1528      free(linkpath);
1529      asprintf(&linkpath, "%s/append-only.d/file.symlink-%d", dir, getuid());
1530      if (symlink(path, linkpath) == -1) {
1531           fprintf(stderr, "symlink(%s, %s) failed: %s\n", path, linkpath, strerror(errno));
1532           fail++;
1533      } else if (unlink(linkpath) != -1) {
1534           fprintf(stderr, "unlink(%s) did not fail\n", linkpath);
1535           fail++;
1536      }
1537
1538      free(linkpath);
1539      asprintf(&linkpath, "%s/append-only.d/file.newname", dir);
1540      if (rename(path, linkpath) != -1) {
1541           fprintf(stderr, "rename(%s, %s) did not fail\n", path, linkpath);
1542           fail++;
1543           rename(linkpath, path);
1544      } else if (errno != EPERM) {
1545           fprintf(stderr, "rename(%s, %s) did not set errno == EPERM\n", path, linkpath);
1546           fail++;
1547      }
1548      free(linkpath);
1549
1550      if (unlink(path) != -1) {
1551           fprintf(stderr, "unlink(%s) did not fail\n", path);
1552           fail++;
1553      } else if (errno != EPERM) {
1554           fprintf(stderr, "unlink(%s) did not set errno == EPERM\n", path);
1555           fail++;
1556      }
1557
1558      free(path);
1559      asprintf(&path, "%s/append-only.d/newfile-%d", dir, getuid());
1560      errno = 0;
1561      if ((fd = open(path, O_RDWR|O_CREAT, 0666)) == -1) {
1562           fprintf(stderr, "open(%s, O_RDWR|O_CREAT, 0666) failed: %s\n", path, strerror(errno));
1563           fail++;
1564      } else if (unlink(path) != -1) {
1565           fprintf(stderr, "unlink(%s) did not fail\n", path);
1566           fail++;
1567           close(fd);
1568      } else
1569           close(fd);
1570
1571      if (!getuid()) {
1572           free(path);
1573           asprintf(&path, "%s/append-only.d/newdev-%d", dir, getuid());
1574           if (stat("/dev/null", &st) != -1) {
1575                if (mknod(path, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, st.st_rdev) == -1) {
1576                     fprintf(stderr, "mknod(%s, S_IFCHR|0666, %lld) failed: %s\n", path, (long long int)st.st_rdev, strerror(errno));
1577                     fail++;
1578                } else if (unlink(path) != -1) {
1579                     fprintf(stderr, "unlink(%s) did not fail\n", path);
1580                     fail++;
1581                }
1582           }
1583      }
1584
1585      free(path);
1586      asprintf(&path, "%s/append-only.d/newdir-%d", dir, getuid());
1587      if (mkdir(path, 0777) == -1) {
1588           fprintf(stderr, "mkdir(%s, 0777) failed: %s\n", path, strerror(errno));
1589           fail++;
1590      } else if (rmdir(path) != -1) {
1591           fprintf(stderr, "rmdir(%s) did not fail\n", path);
1592           fail++;
1593      }
1594
1595      free(path);
1596      asprintf(&path, "%s/append-only.d/newdir-%d/newfile-%d", dir, getuid(), getuid());
1597      if ((fd = open(path, O_RDWR|O_CREAT, 0666)) == -1) {
1598           fprintf(stderr, "open(%s, O_RDWR|O_CREAT, 0666) failed: %s\n", path, strerror(errno));
1599           fail++;
1600      } else {
1601           if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) {
1602                fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, (int)strlen(scribble), strerror(errno));
1603                fail++;
1604           }
1605           close(fd);
1606      }
1607      if (!getuid()) {
1608           if (chmod(path, 0700) == -1) {
1609                fprintf(stderr, "chmod(%s, 0700) failed: %s\n", path, strerror(errno));
1610                fail++;
1611           } else
1612                chmod(path, 0666);
1613
1614           if (chown(path, 1, 1) == -1) {
1615                fprintf(stderr, "chown(%s, 1, 1) failed: %s\n", path, strerror(errno));
1616                fail++;
1617           } else
1618                chown(path, 0, 0);
1619      }
1620
1621      free(path);
1622      asprintf(&path, "%s/append-only.d/dir/newfile-%d", dir, getuid());
1623      if ((fd = open(path, O_RDWR|O_CREAT, 0666)) == -1) {
1624           fprintf(stderr, "open(%s, O_RDWR|O_CREAT, 0666) failed: %s\n", path, strerror(errno));
1625           fail++;
1626      } else {
1627           if (write(fd, scribble, strlen(scribble)) != strlen(scribble)) {
1628                fprintf(stderr, "write(%s, %s, %d) failed: %s\n", path, scribble, (int)strlen(scribble), strerror(errno));
1629                fail++;
1630           }
1631           close(fd);
1632      }
1633      if (!getuid()) {
1634           if (chmod(path, 0700) == -1) {
1635                fprintf(stderr, "chmod(%s, 0700) failed: %s\n", path, strerror(errno));
1636                fail++;
1637           } else
1638                chmod(path, 0666);
1639
1640           if (chown(path, 1, 1) == -1) {
1641                fprintf(stderr, "chown(%s, 1, 1) failed: %s\n", path, strerror(errno));
1642                fail++;
1643           } else
1644                chown(path, 0, 0);
1645      }
1646
1647      free(path);
1648      asprintf(&path, "%s/append-only.d/dir", dir);
1649      errno = 0;
1650      if (rmdir(path) != -1) {
1651           fprintf(stderr, "rmdir(%s) did not fail\n", path);
1652           fail++;
1653      } else if (errno != EPERM) {
1654           fprintf(stderr, "rmdir(%s) did not set errno == EPERM\n", path);
1655           fail++;
1656      }
1657
1658      free(path);
1659      asprintf(&path, "%s/append-only.d", dir);
1660 #ifdef TEST_UTIME
1661      errno = 0;
1662      if (utime(path, &tbuf) != -1) {
1663           fprintf(stderr, "utime(%s, <epoch>) did not fail\n", path);
1664           fail++;
1665      } else if (errno != EPERM) {
1666           fprintf(stderr, "utime(%s, <epoch>) did not set errno == EPERM\n", path);
1667           fail++;
1668      }
1669
1670      errno = 0;
1671      if (utime(path, NULL) == -1) {
1672           fprintf(stderr, "utime(%s, NULL) failed\n", path);
1673           fail++;
1674      }
1675 #endif /* TEST_UTIME */
1676
1677      if (!getuid()) { /* these would fail if not root anyway */
1678           errno = 0;
1679           if (chmod(path, 7777) != -1) {
1680                fprintf(stderr, "chmod(%s, 7777) did not fail\n", path);
1681                fail++;
1682                chmod(path, 0666);
1683           } else if (errno != EPERM) {
1684                fprintf(stderr, "chmod(%s, 7777) did not set errno == EPERM\n", path);
1685                fail++;
1686           }
1687
1688           errno = 0;
1689           if (chown(path, 1, 1) != -1) {
1690                fprintf(stderr, "chown(%s, 1, 1) did not fail\n", path);
1691                fail++;
1692                chown(path, 0, 0);
1693           } else if (errno != EPERM) {
1694                fprintf(stderr, "chown(%s, 1, 1) did not set errno to EPERM\n", path);
1695                fail++;
1696           }
1697           errno = 0;
1698           if (del_acl(path) != 1) {
1699                if (errno != EOPNOTSUPP) {
1700                fprintf(stderr, "del_acl(%s) did not fail\n", path);
1701                fail++;
1702                }
1703           } else if (errno != EPERM) {
1704                fprintf(stderr, "del_acl(%s) did not set errno == EPERM\n", path);
1705                fail++;
1706           }
1707           errno = 0;
1708           if (add_acl(path, acl_text) != 1) {
1709                if (errno != EOPNOTSUPP) {
1710                fprintf(stderr, "add_acl(%s) did not fail\n", path);
1711                fail++;
1712                }
1713           } else if (errno != EPERM) {
1714                fprintf(stderr, "add_acl(%s) did not set errno == EPERM\n", path);
1715                fail++;
1716           }
1717
1718           if (setxattr(path, "trusted.test", "scribble", strlen("scribble"), XATTR_REPLACE) != -1) {
1719                fprintf(stderr, "setxattr(%s, trusted.test, scribble, 8, XATTR_REPLACE) did not fail\n", path);
1720                fail++;
1721           } else if (errno != EPERM && errno != EOPNOTSUPP) {
1722                fprintf(stderr,
1723                        "setxattr(%s, trusted.test, scribble, 8, XATTR_REPLACE) did not set errno == EPERM\n", path);
1724                fail++;
1725           }
1726           if (setxattr(path, "trusted.scribble", "scribble", strlen("scribble"), XATTR_CREATE) != -1) {
1727                fprintf(stderr, "setxattr(%s, trusted.scribble, scribble, 8, XATTR_CREATE) did not fail\n", path);
1728                fail++;
1729           } else if (errno != EPERM && errno != EOPNOTSUPP) {
1730                fprintf(stderr,
1731                        "setxattr(%s, trusted.scribble, scribble, 8, XATTR_CREATE) did not set errno == EPERM\n", path);
1732                fail++;
1733           }
1734           if (removexattr(path, "trusted.test") != -1) {
1735                fprintf(stderr, "removexattr(%s, trusted.test) did not fail\n", path);
1736                fail++;
1737           } else if (errno != EPERM && errno != EOPNOTSUPP) {
1738                fprintf(stderr,
1739                        "removexattr(%s, trusted.test) did not set errno == EPERM\n", path);
1740                fail++;
1741           }
1742      }
1743
1744      if (setxattr(path, "user.test", "scribble", strlen("scribble"), XATTR_REPLACE) != -1) {
1745           fprintf(stderr, "setxattr(%s, user.test, scribble, 8, XATTR_REPLACE) did not fail\n", path);
1746           fail++;
1747      } else if (errno != EPERM && errno != EOPNOTSUPP) {
1748           fprintf(stderr, "setxattr(%s, user.test, scribble, 8, XATTR_REPLACE) did not set errno == EPERM\n", path);
1749           fail++;
1750      }
1751      if (setxattr(path, "user.scribble", "scribble", strlen("scribble"), XATTR_CREATE) != -1) {
1752           fprintf(stderr, "setxattr(%s, user.scribble, scribble, 8, XATTR_CREATE) did not fail\n", path);
1753           fail++;
1754      } else if (errno != EPERM && errno != EOPNOTSUPP) {
1755           fprintf(stderr, "setxattr(%s, user.scribble, scribble, 8, XATTR_CREATE) did not set errno == EPERM\n", path);
1756           fail++;
1757      }
1758      if (removexattr(path, "user.test") != -1) {
1759           fprintf(stderr, "removexattr(%s, user.test) did not fail\n", path);
1760           fail++;
1761      } else if (errno != EPERM && errno != EOPNOTSUPP) {
1762           perror("removexattr");
1763           fprintf(stderr,
1764                   "removexattr(%s, user.test) did not set errno == EPERM\n", path);
1765           fail++;
1766      }
1767
1768      free(path);
1769      asprintf(&path, "%s/empty-append-only.d", dir);
1770      errno = 0;
1771      if (rmdir(path) != -1) {
1772           fprintf(stderr, "rmdir(%s) did not fail\n", path);
1773           fail++;
1774      } else if (errno != EPERM) {
1775           fprintf(stderr, "rmdir(%s) did not set errno == EPERM\n", path);
1776           fail++;
1777      }
1778
1779      free(path);
1780      return fail;
1781 }
1782
1783 static int check_test_area(const char *dir)
1784 {
1785      char *path;
1786      struct stat st;
1787
1788      asprintf(&path, "%s/", dir);
1789      if (stat(path, &st) == -1) {
1790           fprintf(stderr, "%s: %s: %s\n", __progname, path, strerror(errno));
1791           return 1;
1792      }
1793      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) {
1794           fprintf(stderr, "%s: %s needs to be rwx for for all, and owner uid should be 0\n",
1795                   __progname, path);
1796           return 1;
1797      }
1798
1799      free(path);
1800      asprintf(&path, "%s/immutable.f", dir);
1801      if (stat(path, &st) == -1) {
1802           perror(path);
1803           return 1;
1804      }
1805      if (!(st.st_mode & (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) || st.st_uid || !S_ISREG(st.st_mode)) {
1806           fprintf(stderr, "%s: %s needs to be a regular file, rw for all, and owner uid should be 0\n",
1807                   __progname, path);
1808           return 1;
1809      }
1810
1811      free(path);
1812      asprintf(&path, "%s/append-only.f", dir);
1813      if (stat(path, &st) == -1) {
1814           perror(path);
1815           return 1;
1816      }
1817      if (!(st.st_mode & (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) || st.st_uid || !S_ISREG(st.st_mode)) {
1818           fprintf(stderr, "%s: %s needs to be a regular file, rw for all, and owner uid should be 0\n",
1819                   __progname, path);
1820           return 1;
1821      }
1822
1823      free(path);
1824      asprintf(&path, "%s/immutable.d", dir);
1825      if (stat(path, &st) == -1) {
1826           perror(path);
1827           return 1;
1828      }
1829      if (!(st.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH)) ||
1830          st.st_uid || !S_ISDIR(st.st_mode)) {
1831           fprintf(stderr, "%s: %s needs to be a directory, rwx for all, and owner uid should be 0\n",
1832                   __progname, path);
1833           return 1;
1834      }
1835
1836      free(path);
1837      asprintf(&path, "%s/append-only.d", dir);
1838      if (stat(path, &st) == -1) {
1839           perror(path);
1840           return 1;
1841      }
1842      if (!(st.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH)) ||
1843          st.st_uid || !S_ISDIR(st.st_mode)) {
1844           fprintf(stderr, "%s: %s needs to be a directory, rwx for all, and owner uid should be 0\n",
1845                   __progname, path);
1846           return 1;
1847      }
1848
1849      free(path);
1850      asprintf(&path, "%s/immutable.d/file", dir);
1851      if (stat(path, &st) == -1) {
1852           perror(path);
1853           return 1;
1854      }
1855      if (!(st.st_mode & (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) || st.st_uid || !S_ISREG(st.st_mode)) {
1856           fprintf(stderr, "%s: %s needs to be a regular file, rw for all, and owner uid should be 0\n",
1857                   __progname, path);
1858           return 1;
1859      }
1860
1861      free(path);
1862      asprintf(&path, "%s/append-only.d/file", dir);
1863      if (stat(path, &st) == -1) {
1864           perror(path);
1865           return 1;
1866      }
1867      if (!(st.st_mode & (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) || st.st_uid || !S_ISREG(st.st_mode)) {
1868           fprintf(stderr, "%s: %s needs to be a regular file, rw for all, and owner uid should be 0\n",
1869                   __progname, path);
1870           return 1;
1871      }
1872
1873      free(path);
1874      asprintf(&path, "%s/immutable.d/dir", dir);
1875      if (stat(path, &st) == -1) {
1876           perror(path);
1877           return 1;
1878      }
1879      if (!(st.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH)) ||
1880          st.st_uid || !S_ISDIR(st.st_mode)) {
1881           fprintf(stderr, "%s: %s needs to be a directory, rwx for all, and owner uid should be 0\n",
1882                   __progname, path);
1883           return 1;
1884      }
1885
1886      free(path);
1887      asprintf(&path, "%s/append-only.d/dir", dir);
1888      if (stat(path, &st) == -1) {
1889           perror(path);
1890           return 1;
1891      }
1892      if (!(st.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH)) ||
1893          st.st_uid || !S_ISDIR(st.st_mode)) {
1894           fprintf(stderr, "%s: %s needs to be a directory, rwx for all, and owner uid should be 0\n",
1895                   __progname, path);
1896           return 1;
1897      }
1898      return 0;
1899 }
1900
1901 static int create_dir(char **ppath, const char *fmt, const char *dir)
1902 {
1903      const char *path;
1904      struct stat st;
1905
1906      if (asprintf(ppath, fmt, dir) == -1) {
1907           return 1;
1908      }
1909      path = *ppath;
1910      if (stat(path, &st) == 0) {
1911           fprintf(stderr, "%s: Test area directory %s must not exist for test area creation.\n",
1912                   __progname, path);
1913           return 1;
1914      }
1915      if (mkdir(path, 0777) != 0) {
1916           fprintf(stderr, "%s: error creating directory %s: %s\n", __progname, path, strerror(errno));
1917           return 1;
1918      }
1919      return 0;
1920 }
1921
1922 static int create_file(char **ppath, const char *fmt, const char *dir)
1923 {
1924      const char *path;
1925      int fd;
1926
1927      if (asprintf(ppath, fmt, dir) == -1) {
1928           return 1;
1929      }
1930      path = *ppath;
1931      if ((fd = open(path, O_WRONLY|O_CREAT|O_EXCL, 0666)) == -1) {
1932           fprintf(stderr, "%s: error creating file %s: %s\n", __progname, path, strerror(errno));
1933           return 1;
1934      }
1935      return fd;
1936 }
1937
1938 static int create_xattrs(int fd)
1939 {
1940      if (fsetxattr(fd, "trusted.test", "readonly", strlen("readonly"), XATTR_CREATE) != 0) {
1941           if (errno != EOPNOTSUPP) {
1942                perror("setxattr");
1943                return 1;
1944           }
1945      }
1946      if (fsetxattr(fd, "user.test", "readonly", strlen("readonly"), XATTR_CREATE) != 0) {
1947           if (errno != EOPNOTSUPP) {
1948                perror("setxattr");
1949                return 1;
1950           }
1951      }
1952      return 0;
1953 }
1954
1955 static int create_test_area(const char *dir)
1956 {
1957      int fd;
1958      char *path;
1959      static const char *acl_u_text = "u::rw-,g::rw-,o::rw-,u:nobody:rw-,m::rw-";
1960      static const char *acl_u_text_d = "u::rwx,g::rwx,o::rwx,u:nobody:rwx,m::rwx";
1961      static const char *immutable = "This is an immutable file.\nIts contents cannot be altered.\n";
1962      static const char *append_only = "This is an append-only file.\nIts contents cannot be altered.\n"
1963           "Data can only be appended.\n---\n";
1964
1965      if (getuid()) {
1966           fprintf(stderr, "%s: you are not root, go away.\n", __progname);
1967           return 1;
1968      }
1969
1970      umask(0000);
1971      if (create_dir(&path, "%s", dir)) {
1972           return 1;
1973      }
1974      free(path);
1975
1976      if (create_dir(&path, "%s/append-only.d", dir)) {
1977           return 1;
1978      }
1979      free(path);
1980
1981      if (create_dir(&path, "%s/append-only.d/dir", dir)) {
1982           return 1;
1983      }
1984      free(path);
1985
1986      if ((fd = create_file(&path, "%s/append-only.d/file", dir)) == -1) {
1987           return 1;
1988      }
1989      close(fd);
1990      free(path);
1991
1992      if (create_dir(&path, "%s/immutable.d", dir)) {
1993           return 1;
1994      }
1995      free(path);
1996
1997      if (create_dir(&path, "%s/immutable.d/dir", dir)) {
1998           return 1;
1999      }
2000      free(path);
2001
2002      if ((fd = create_file(&path, "%s/immutable.d/file", dir)) == -1) {
2003           return 1;
2004      }
2005      close(fd);
2006      free(path);
2007
2008      if ((fd = create_file(&path, "%s/immutable.f", dir)) == -1) {
2009           return 1;
2010      }
2011      if (write(fd, immutable, strlen(immutable)) != strlen(immutable)) {
2012           fprintf(stderr, "%s: error writing file %s: %s\n", __progname, path, strerror(errno));
2013           return 1;
2014      }
2015      if (fadd_acl(fd, acl_u_text)) {
2016           perror("acl");
2017           return 1;
2018      }
2019      if (create_xattrs(fd)) {
2020           return 1;
2021      }
2022      if (fsetflag(path, fd, 1, 1)) {
2023           perror("fsetflag");
2024           close(fd);
2025           return 1;
2026      }
2027      close(fd);
2028      free(path);
2029
2030      if ((fd = create_file(&path, "%s/append-only.f", dir)) == -1) {
2031           fprintf(stderr, "%s: error creating file %s: %s\n", __progname, path, strerror(errno));
2032           return 1;
2033      }
2034      if (write(fd, append_only, strlen(append_only)) != strlen(append_only)) {
2035           fprintf(stderr, "%s: error writing file %s: %s\n", __progname, path, strerror(errno));
2036           return 1;
2037      }
2038      if (fadd_acl(fd, acl_u_text)) {
2039           perror("acl");
2040           return 1;
2041      }
2042      if (create_xattrs(fd)) {
2043           return 1;
2044      }
2045      if (fsetflag(path, fd, 1, 0)) {
2046           perror("fsetflag");
2047           close(fd);
2048           return 1;
2049      }
2050      close(fd);
2051      free(path);
2052
2053      asprintf(&path, "%s/immutable.d", dir);
2054      if ((fd = open(path, O_RDONLY)) == -1) {
2055           fprintf(stderr, "%s: error opening %s: %s\n", __progname, path, strerror(errno));
2056           return 1;
2057      }
2058      if (fadd_acl(fd, acl_u_text_d)) {
2059           perror("acl");
2060           return 1;
2061      }
2062      if (create_xattrs(fd)) {
2063           return 1;
2064      }
2065      if (fsetflag(path, fd, 1, 1)) {
2066           perror("fsetflag");
2067           close(fd);
2068           return 1;
2069      }
2070      close(fd);
2071      free(path);
2072
2073      if (create_dir(&path, "%s/empty-immutable.d", dir)) {
2074           return 1;
2075      }
2076      if ((fd = open(path, O_RDONLY)) == -1) {
2077           fprintf(stderr, "%s: error opening %s: %s\n", __progname, path, strerror(errno));
2078           return 1;
2079      }
2080      if (fsetflag(path, fd, 1, 1)) {
2081           perror("fsetflag");
2082           close(fd);
2083           return 1;
2084      }
2085      close(fd);
2086      free(path);
2087
2088      asprintf(&path, "%s/append-only.d", dir);
2089      if ((fd = open(path, O_RDONLY)) == -1) {
2090           fprintf(stderr, "%s: error opening %s: %s\n", __progname, path, strerror(errno));
2091           return 1;
2092      }
2093      if (fadd_acl(fd, acl_u_text_d)) {
2094           perror("acl");
2095           return 1;
2096      }
2097      if (create_xattrs(fd)) {
2098           return 1;
2099      }
2100      if (fsetflag(path, fd, 1, 0)) {
2101           perror("fsetflag");
2102           close(fd);
2103           return 1;
2104      }
2105      close(fd);
2106      free(path);
2107
2108      if (create_dir(&path, "%s/empty-append-only.d", dir)) {
2109           return 1;
2110      }
2111      if ((fd = open(path, O_RDONLY)) == -1) {
2112           fprintf(stderr, "%s: error opening %s: %s\n", __progname, path, strerror(errno));
2113           return 1;
2114      }
2115      if (fsetflag(path, fd, 1, 0)) {
2116           perror("fsetflag");
2117           close(fd);
2118           return 1;
2119      }
2120      close(fd);
2121      free(path);
2122      return 0;
2123 }
2124
2125 static int remove_test_area(const char *dir)
2126 {
2127      int fd;
2128      int ret;
2129      int err = 0;
2130      char *path;
2131      pid_t pid;
2132      struct stat st;
2133
2134      if (getuid()) {
2135           fprintf(stderr, "%s: you are not root, go away.\n", __progname);
2136           return 1;
2137      }
2138
2139      if (stat(dir, &st) == -1) {
2140           fprintf(stderr, "%s: cannot remove test area %s: %s\n", __progname, dir, strerror(errno));
2141           return 1;
2142      }
2143
2144      asprintf(&path, "%s/immutable.d", dir);
2145      if ((fd = open(path, O_RDONLY)) == -1) {
2146           fprintf(stderr, "%s: error opening %s: %s\n", __progname, path, strerror(errno));
2147           err = 1;
2148      } else {
2149           if (fsetflag(path, fd, 0, 1))
2150                perror("fsetflag");
2151           close(fd);
2152      }
2153      free(path);
2154
2155      asprintf(&path, "%s/empty-immutable.d", dir);
2156      if ((fd = open(path, O_RDONLY)) == -1) {
2157           fprintf(stderr, "%s: error opening %s: %s\n", __progname, path, strerror(errno));
2158           err = 1;
2159      } else {
2160           if (fsetflag(path, fd, 0, 1))
2161                perror("fsetflag");
2162
2163           close(fd);
2164      }
2165      free(path);
2166
2167      asprintf(&path, "%s/append-only.d", dir);
2168      if ((fd = open(path, O_RDONLY)) == -1) {
2169           fprintf(stderr, "%s: error opening %s: %s\n", __progname, path, strerror(errno));
2170           err = 1;
2171      } else {
2172           if (fsetflag(path, fd, 0, 0))
2173                perror("fsetflag");
2174           close(fd);
2175      }
2176      free(path);
2177
2178      asprintf(&path, "%s/empty-append-only.d", dir);
2179      if ((fd = open(path, O_RDONLY)) == -1) {
2180           fprintf(stderr, "%s: error opening %s: %s\n", __progname, path, strerror(errno));
2181           err = 1;
2182      } else {
2183           if (fsetflag(path, fd, 0, 0))
2184                perror("fsetflag");
2185           close(fd);
2186      }
2187      free(path);
2188
2189      asprintf(&path, "%s/append-only.f", dir);
2190      if ((fd = open(path, O_RDONLY)) == -1) {
2191           fprintf(stderr, "%s: error opening %s: %s\n", __progname, path, strerror(errno));
2192           err = 1;
2193      } else {
2194           if (fsetflag(path, fd, 0, 0))
2195                perror("fsetflag");
2196
2197           close(fd);
2198      }
2199      free(path);
2200
2201      asprintf(&path, "%s/immutable.f", dir);
2202      if ((fd = open(path, O_RDONLY)) == -1) {
2203           fprintf(stderr, "%s: error opening %s: %s\n", __progname, path, strerror(errno));
2204           err = 1;
2205      } else {
2206           if (fsetflag(path, fd, 0, 1))
2207                perror("fsetflag");
2208           close(fd);
2209      }
2210      free(path);
2211
2212      if (err) {
2213           fprintf(stderr, "%s: Warning, expected parts of the test area missing, not removing.\n", __progname);
2214           return 1;
2215      }
2216
2217      pid = fork();
2218      if (!pid) {
2219           execl("/bin/rm", "rm", "-rf", dir, NULL);
2220           return 1;
2221      } else if (pid == -1) {
2222           perror("fork failed");
2223           return 1;
2224      }
2225      wait(&ret);
2226
2227      return WEXITSTATUS(ret);
2228 }
2229
2230 int main(int argc, char **argv)
2231 {
2232      int ret;
2233      int failed = 0;
2234      int runtest = 1, create = 0, remove = 0;
2235
2236 /* this arg parsing is gross, but who cares, its a test program */
2237
2238      if (argc < 2) {
2239           fprintf(stderr, "usage: t_immutable [-C|-c|-r] test_area_dir\n");
2240           return 1;
2241      }
2242
2243      if (!strcmp(argv[1], "-c")) {
2244           create = 1;
2245      } else if (!strcmp(argv[1], "-C")) {
2246           /* Prepare test area without running tests */
2247           create = 1;
2248           runtest = 0;
2249      } else if (!strcmp(argv[1], "-r")) {
2250           remove = 1;
2251      }
2252
2253      if (argc != 2 + (create | remove)) {
2254           fprintf(stderr, "usage: t_immutable [-C|-c|-r] test_area_dir\n");
2255           return 1;
2256      }
2257
2258      if (create) {
2259           ret = create_test_area(argv[argc-1]);
2260           if (ret || !runtest) {
2261                return ret;
2262           }
2263      } else if (remove) {
2264           return remove_test_area(argv[argc-1]);
2265      }
2266
2267      umask(0000);
2268
2269      if (check_test_area(argv[argc-1]))
2270           return 1;
2271
2272      printf("testing immutable...");
2273      if ((ret = test_immutable(argv[argc-1])) != 0) {
2274           printf("FAILED! (%d tests failed)\n", ret);
2275           failed = 1;
2276      } else
2277           puts("PASS.");
2278
2279      printf("testing append-only...");
2280      if ((ret = test_append(argv[argc-1])) != 0) {
2281           printf("FAILED! (%d tests failed)\n", ret);
2282           failed = 1;
2283      } else
2284           puts("PASS.");
2285
2286      if (!getuid() && !failed) {
2287           if (setgroups(0, NULL) != 0)
2288                perror("setgroups");
2289           if (setgid(65534) != 0)
2290                perror("setgid");
2291           if (setuid(65534) != 0)
2292                perror("setuid");
2293           printf("testing immutable as non-root...");
2294           if ((ret = test_immutable(argv[argc-1])) != 0) {
2295                printf("FAILED! (%d tests failed)\n", ret);
2296                failed = 1;
2297           } else
2298                puts("PASS.");
2299
2300           printf("testing append-only as non-root...");
2301           if ((ret = test_append(argv[argc-1])) != 0) {
2302                printf("FAILED! (%d tests failed)\n", ret);
2303                failed = 1;
2304           } else
2305                puts("PASS.");
2306      }
2307
2308      return failed;
2309 }