]> git.saurik.com Git - apple/copyfile.git/blob - copyfile_test/readonly_fd_test.c
copyfile-173.40.2.tar.gz
[apple/copyfile.git] / copyfile_test / readonly_fd_test.c
1 //
2 // Copyright (c) 2020 Apple Inc. All rights reserved.
3 //
4
5 #include <err.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <paths.h>
9 #include <stdbool.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <sys/types.h>
13 #include <sys/acl.h>
14 #include <sys/attr.h>
15 #include <sys/stat.h>
16 #include <sys/time.h>
17 #include <sys/xattr.h>
18 #include <unistd.h>
19 #include "readonly_fd_test.h"
20 #include "test_utils.h"
21
22
23 static
24 bool test_readonly_fd_metadata(const char *basedir)
25 {
26 char filename[] = ".readonly-ops-XXXXXX";
27 bool created = false;
28 bool success = true;
29 int dirfd = -1;
30 int tmpfd = -1;
31 int fd = -1;
32 acl_t acl = NULL;
33
34 static const char test_name[] = "readonly_fd_metadata";
35 printf("START [%s]\n", test_name);
36
37 assert_with_errno((dirfd = open(basedir, O_RDONLY | O_DIRECTORY)) != -1);
38 assert_with_errno((tmpfd = mkstempsat_np(dirfd, filename, 0)) != -1);
39 created = true;
40
41 assert_with_errno((fd = openat(dirfd, filename, O_RDONLY)) != -1);
42 close(tmpfd);
43 tmpfd = -1;
44
45 // confirm that writes are disallowed
46 const char data[] = "failure";
47 assert(write(fd, data, sizeof(data) - 1) == -1);
48
49 // check fchown()
50 const uid_t uid = geteuid();
51 const gid_t gid = getegid();
52 assert_no_err(fchown(fd, uid, gid));
53
54 // check fchmod()
55 assert_no_err(fchmod(fd, 0644));
56 assert_no_err(fchmod(fd, 0600));
57
58 // check fchflags()
59 assert_no_err(fchflags(fd, UF_HIDDEN));
60
61 // check setting timestamps with fsetattrlist
62 const time_t mtime = 978307200;
63 const time_t atime = mtime + 1;
64
65 struct timeval matimes_usec[] = {{mtime, 0}, {atime, 0}};
66 assert_no_err(futimes(fd, matimes_usec));
67
68 struct attrlist attrlist = {
69 .bitmapcount = ATTR_BIT_MAP_COUNT,
70 .commonattr = ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME,
71 };
72 struct {
73 struct timespec mtime;
74 struct timespec atime;
75 } matimes_nsec = {{mtime, 0}, {atime, 0}};
76 assert_no_err(fsetattrlist(fd, &attrlist, &matimes_nsec, sizeof(matimes_nsec), 0));
77
78 // check adding and removing xattrs
79 static const char key[] = "local.test-xattr";
80 static const char value[] = "local.test-xattr.value";
81 assert_no_err(fsetxattr(fd, key, value, sizeof(value)-1, 0, 0));
82 assert_no_err(fremovexattr(fd, key, 0));
83
84 // check setting ACLs
85 assert_with_errno((acl = acl_init(1)) != NULL);
86 assert_no_err(acl_set_fd(fd, acl));
87
88 // log pass/fail before cleanup
89 if (success) {
90 printf("PASS [%s]\n", test_name);
91 } else {
92 printf("FAIL [%s]\n", test_name);
93 }
94
95 // clean up resources
96 if (acl) {
97 acl_free(acl);
98 }
99 if (fd != -1) {
100 close(fd);
101 }
102 if (tmpfd != -1) {
103 close(tmpfd);
104 }
105 if (created) {
106 unlinkat(dirfd, filename, 0);
107 }
108 if (dirfd != -1) {
109 close(dirfd);
110 }
111
112 return success;
113 }
114
115
116 bool do_readonly_fd_test(const char *apfs_test_directory, size_t block_size __unused)
117 {
118 // These tests verify the underlying calls needed for COPYFILE_METADATA
119 // operations are safe with O_RDONLY file descriptors. If this fails,
120 // expect <rdar://60074298> to cause many other copyfile() failures.
121 bool success = true;
122 success = success && test_readonly_fd_metadata(apfs_test_directory);
123 return !success; // caller expects nonzero to mean failure
124 }