1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <gtest/gtest.h>
18
19#include <errno.h>
20#include <fcntl.h>
21#include <stdlib.h>
22#include <sys/stat.h>
23
24#include "TemporaryFile.h"
25
26TEST(sys_stat, futimens) {
27  FILE* fp = tmpfile();
28  ASSERT_TRUE(fp != NULL);
29
30  int fd = fileno(fp);
31  ASSERT_NE(fd, -1);
32
33  timespec times[2];
34  times[0].tv_sec = 123;
35  times[0].tv_nsec = 0;
36  times[1].tv_sec = 456;
37  times[1].tv_nsec = 0;
38  ASSERT_EQ(0, futimens(fd, times)) << strerror(errno);
39
40  struct stat sb;
41  ASSERT_EQ(0, fstat(fd, &sb));
42  ASSERT_EQ(times[0].tv_sec, static_cast<long>(sb.st_atime));
43  ASSERT_EQ(times[1].tv_sec, static_cast<long>(sb.st_mtime));
44
45  fclose(fp);
46}
47
48TEST(sys_stat, futimens_EBADF) {
49  timespec times[2];
50  times[0].tv_sec = 123;
51  times[0].tv_nsec = 0;
52  times[1].tv_sec = 456;
53  times[1].tv_nsec = 0;
54  ASSERT_EQ(-1, futimens(-1, times));
55  ASSERT_EQ(EBADF, errno);
56}
57
58TEST(sys_stat, mkfifo_failure) {
59  errno = 0;
60  ASSERT_EQ(-1, mkfifo("/", 0666));
61  ASSERT_EQ(EEXIST, errno);
62}
63
64TEST(sys_stat, mkfifoat_failure) {
65  errno = 0;
66  ASSERT_EQ(-1, mkfifoat(-2, "x", 0666));
67  ASSERT_EQ(EBADF, errno);
68}
69
70TEST(sys_stat, mkfifo) {
71  if (getuid() == 0) {
72    // Racy but probably sufficient way to get a suitable filename.
73    std::string path;
74    {
75      TemporaryFile tf;
76      path = tf.filename;
77    }
78
79    ASSERT_EQ(0, mkfifo(path.c_str(), 0666));
80    struct stat sb;
81    ASSERT_EQ(0, stat(path.c_str(), &sb));
82    ASSERT_TRUE(S_ISFIFO(sb.st_mode));
83    unlink(path.c_str());
84  } else {
85    // SELinux policy forbids us from creating FIFOs. http://b/17646702.
86    GTEST_LOG_(INFO) << "This test only performs a test when run as root.";
87  }
88}
89
90TEST(sys_stat, stat64_lstat64_fstat64) {
91  struct stat64 sb;
92  ASSERT_EQ(0, stat64("/proc/version", &sb));
93  ASSERT_EQ(0, lstat64("/proc/version", &sb));
94  int fd = open("/proc/version", O_RDONLY);
95  ASSERT_EQ(0, fstat64(fd, &sb));
96  close(fd);
97}
98
99TEST(sys_stat, fchmodat_EFAULT_file) {
100  ASSERT_EQ(-1, fchmodat(AT_FDCWD, (char *) 0x1, 0751, 0));
101  ASSERT_EQ(EFAULT, errno);
102}
103
104TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_EFAULT_file) {
105  ASSERT_EQ(-1, fchmodat(AT_FDCWD, (char *) 0x1, 0751, AT_SYMLINK_NOFOLLOW));
106#if defined(__BIONIC__)
107  ASSERT_EQ(EFAULT, errno);
108#else
109  // glibc 2.19 does not implement AT_SYMLINK_NOFOLLOW and always
110  // returns ENOTSUP
111  ASSERT_EQ(ENOTSUP, errno);
112#endif
113}
114
115TEST(sys_stat, fchmodat_bad_flags) {
116  ASSERT_EQ(-1, fchmodat(AT_FDCWD, "/blah", 0751, ~AT_SYMLINK_NOFOLLOW));
117  ASSERT_EQ(EINVAL, errno);
118}
119
120TEST(sys_stat, fchmodat_bad_flags_ALL) {
121  ASSERT_EQ(-1, fchmodat(AT_FDCWD, "/blah", 0751, ~0));
122  ASSERT_EQ(EINVAL, errno);
123}
124
125TEST(sys_stat, fchmodat_nonexistant_file) {
126  ASSERT_EQ(-1, fchmodat(AT_FDCWD, "/blah", 0751, 0));
127  ASSERT_EQ(ENOENT, errno);
128}
129
130TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_nonexistant_file) {
131  ASSERT_EQ(-1, fchmodat(AT_FDCWD, "/blah", 0751, AT_SYMLINK_NOFOLLOW));
132#if defined(__BIONIC__)
133  ASSERT_EQ(ENOENT, errno);
134#else
135  // glibc 2.19 does not implement AT_SYMLINK_NOFOLLOW and always
136  // returns ENOTSUP
137  ASSERT_EQ(ENOTSUP, errno);
138#endif
139}
140
141static void AssertFileModeEquals(mode_t expected_mode, const char* filename) {
142  struct stat sb;
143  ASSERT_EQ(0, stat(filename, &sb));
144  mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO;
145  ASSERT_EQ(expected_mode & mask, static_cast<mode_t>(sb.st_mode) & mask);
146}
147
148TEST(sys_stat, fchmodat_file) {
149  TemporaryFile tf;
150
151  ASSERT_EQ(0, fchmodat(AT_FDCWD, tf.filename, 0751, 0));
152  AssertFileModeEquals(0751, tf.filename);
153}
154
155TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_file) {
156  TemporaryFile tf;
157  errno = 0;
158  int result = fchmodat(AT_FDCWD, tf.filename, 0751, AT_SYMLINK_NOFOLLOW);
159
160#if defined(__BIONIC__)
161  ASSERT_EQ(0, result);
162  ASSERT_EQ(0, errno);
163  AssertFileModeEquals(0751, tf.filename);
164#else
165  // glibc 2.19 does not implement AT_SYMLINK_NOFOLLOW and always
166  // returns ENOTSUP
167  ASSERT_EQ(-1, result);
168  ASSERT_EQ(ENOTSUP, errno);
169#endif
170}
171
172TEST(sys_stat, fchmodat_symlink) {
173  TemporaryFile tf;
174  char linkname[255];
175
176  snprintf(linkname, sizeof(linkname), "%s.link", tf.filename);
177
178  ASSERT_EQ(0, symlink(tf.filename, linkname));
179  ASSERT_EQ(0, fchmodat(AT_FDCWD, linkname, 0751, 0));
180  AssertFileModeEquals(0751, tf.filename);
181  unlink(linkname);
182}
183
184TEST(sys_stat, fchmodat_dangling_symlink) {
185  TemporaryFile tf;
186  char linkname[255];
187  char target[255];
188
189  snprintf(linkname, sizeof(linkname), "%s.link", tf.filename);
190  snprintf(target, sizeof(target), "%s.doesnotexist", tf.filename);
191
192  ASSERT_EQ(0, symlink(target, linkname));
193  ASSERT_EQ(-1, fchmodat(AT_FDCWD, linkname, 0751, 0));
194  ASSERT_EQ(ENOENT, errno);
195  unlink(linkname);
196}
197
198static void AssertSymlinkModeEquals(mode_t expected_mode, const char* linkname) {
199  struct stat sb;
200  ASSERT_EQ(0, fstatat(AT_FDCWD, linkname, &sb, AT_SYMLINK_NOFOLLOW));
201  mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO;
202  ASSERT_EQ(expected_mode & mask, static_cast<mode_t>(sb.st_mode) & mask);
203}
204
205TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_with_symlink) {
206  TemporaryFile tf;
207  struct stat tf_sb;
208  ASSERT_EQ(0, stat(tf.filename, &tf_sb));
209
210  char linkname[255];
211  snprintf(linkname, sizeof(linkname), "%s.link", tf.filename);
212
213  ASSERT_EQ(0, symlink(tf.filename, linkname));
214  int result = fchmodat(AT_FDCWD, linkname, 0751, AT_SYMLINK_NOFOLLOW);
215  // It depends on the kernel whether chmod operation on symlink is allowed.
216  if (result == 0) {
217    AssertSymlinkModeEquals(0751, linkname);
218  } else {
219    ASSERT_EQ(-1, result);
220    ASSERT_EQ(ENOTSUP, errno);
221  }
222
223  // Target file mode shouldn't be modified.
224  AssertFileModeEquals(tf_sb.st_mode, tf.filename);
225  unlink(linkname);
226}
227
228TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_with_dangling_symlink) {
229  TemporaryFile tf;
230
231  char linkname[255];
232  char target[255];
233  snprintf(linkname, sizeof(linkname), "%s.link", tf.filename);
234  snprintf(target, sizeof(target), "%s.doesnotexist", tf.filename);
235
236  ASSERT_EQ(0, symlink(target, linkname));
237  int result = fchmodat(AT_FDCWD, linkname, 0751, AT_SYMLINK_NOFOLLOW);
238  // It depends on the kernel whether chmod operation on symlink is allowed.
239  if (result == 0) {
240    AssertSymlinkModeEquals(0751, linkname);
241  } else {
242    ASSERT_EQ(-1, result);
243    ASSERT_EQ(ENOTSUP, errno);
244  }
245
246  unlink(linkname);
247}
248
249TEST(sys_stat, faccessat_EINVAL) {
250  ASSERT_EQ(-1, faccessat(AT_FDCWD, "/dev/null", F_OK, ~AT_SYMLINK_NOFOLLOW));
251  ASSERT_EQ(EINVAL, errno);
252#if defined(__BIONIC__)
253  ASSERT_EQ(-1, faccessat(AT_FDCWD, "/dev/null", ~(R_OK | W_OK | X_OK), 0));
254  ASSERT_EQ(EINVAL, errno);
255#else
256  ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", ~(R_OK | W_OK | X_OK), AT_SYMLINK_NOFOLLOW));
257  ASSERT_EQ(-1, faccessat(AT_FDCWD, "/dev/null", ~(R_OK | W_OK | X_OK), 0));
258  ASSERT_EQ(EINVAL, errno);
259#endif
260}
261
262TEST(sys_stat, faccessat_AT_SYMLINK_NOFOLLOW_EINVAL) {
263#if defined(__BIONIC__)
264  // Android doesn't support AT_SYMLINK_NOFOLLOW
265  ASSERT_EQ(-1, faccessat(AT_FDCWD, "/dev/null", F_OK, AT_SYMLINK_NOFOLLOW));
266  ASSERT_EQ(EINVAL, errno);
267#else
268  ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", F_OK, AT_SYMLINK_NOFOLLOW));
269#endif
270}
271
272TEST(sys_stat, faccessat_dev_null) {
273  ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", F_OK, 0));
274  ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", R_OK, 0));
275  ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", W_OK, 0));
276  ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", R_OK|W_OK, 0));
277}
278
279TEST(sys_stat, faccessat_nonexistant) {
280  ASSERT_EQ(-1, faccessat(AT_FDCWD, "/blah", F_OK, AT_SYMLINK_NOFOLLOW));
281#if defined(__BIONIC__)
282  // Android doesn't support AT_SYMLINK_NOFOLLOW
283  ASSERT_EQ(EINVAL, errno);
284#else
285  ASSERT_EQ(ENOENT, errno);
286#endif
287}
288