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 <errno.h>
18 #include <fcntl.h>
19 #include <stdlib.h>
20 #include <sys/stat.h>
21 
22 #include <android-base/file.h>
23 #include <gtest/gtest.h>
24 
25 #if defined(__BIONIC__)
26 #define HAVE_STATX
27 #elif defined(__GLIBC_PREREQ)
28 #if __GLIBC_PREREQ(2, 28)
29 #define HAVE_STATX
30 #endif
31 #endif
32 
TEST(sys_stat,futimens)33 TEST(sys_stat, futimens) {
34   FILE* fp = tmpfile();
35   ASSERT_TRUE(fp != nullptr);
36 
37   int fd = fileno(fp);
38   ASSERT_NE(fd, -1);
39 
40   timespec times[2];
41   times[0].tv_sec = 123;
42   times[0].tv_nsec = 0;
43   times[1].tv_sec = 456;
44   times[1].tv_nsec = 0;
45   ASSERT_EQ(0, futimens(fd, times)) << strerror(errno);
46 
47   struct stat sb;
48   ASSERT_EQ(0, fstat(fd, &sb));
49   ASSERT_EQ(times[0].tv_sec, static_cast<long>(sb.st_atime));
50   ASSERT_EQ(times[1].tv_sec, static_cast<long>(sb.st_mtime));
51 
52   fclose(fp);
53 }
54 
TEST(sys_stat,futimens_EBADF)55 TEST(sys_stat, futimens_EBADF) {
56   timespec times[2];
57   times[0].tv_sec = 123;
58   times[0].tv_nsec = 0;
59   times[1].tv_sec = 456;
60   times[1].tv_nsec = 0;
61   ASSERT_EQ(-1, futimens(-1, times));
62   ASSERT_EQ(EBADF, errno);
63 }
64 
TEST(sys_stat,mkfifo_failure)65 TEST(sys_stat, mkfifo_failure) {
66   errno = 0;
67   ASSERT_EQ(-1, mkfifo("/", 0666));
68   ASSERT_EQ(EEXIST, errno);
69 }
70 
TEST(sys_stat,mkfifoat_failure)71 TEST(sys_stat, mkfifoat_failure) {
72   errno = 0;
73   ASSERT_EQ(-1, mkfifoat(-2, "x", 0666));
74   ASSERT_EQ(EBADF, errno);
75 }
76 
TEST(sys_stat,mkfifo)77 TEST(sys_stat, mkfifo) {
78   if (getuid() == 0) {
79     // Racy but probably sufficient way to get a suitable filename.
80     std::string path;
81     {
82       TemporaryFile tf;
83       path = tf.path;
84     }
85 
86     ASSERT_EQ(0, mkfifo(path.c_str(), 0666));
87     struct stat sb;
88     ASSERT_EQ(0, stat(path.c_str(), &sb));
89     ASSERT_TRUE(S_ISFIFO(sb.st_mode));
90     unlink(path.c_str());
91   } else {
92     // SELinux policy forbids us from creating FIFOs. http://b/17646702.
93     GTEST_SKIP() << "SELinux policy forbids non-root from creating FIFOs";
94   }
95 }
96 
TEST(sys_stat,stat64_lstat64_fstat64)97 TEST(sys_stat, stat64_lstat64_fstat64) {
98   struct stat64 sb;
99   ASSERT_EQ(0, stat64("/proc/version", &sb));
100   ASSERT_EQ(0, lstat64("/proc/version", &sb));
101   int fd = open("/proc/version", O_RDONLY);
102   ASSERT_EQ(0, fstat64(fd, &sb));
103   close(fd);
104 }
105 
TEST(sys_stat,statx)106 TEST(sys_stat, statx) {
107 #if defined(HAVE_STATX)
108   struct statx sx;
109   int rc = statx(AT_FDCWD, "/proc/version", AT_STATX_SYNC_AS_STAT, STATX_ALL, &sx);
110   if (rc == -1 && errno == ENOSYS) {
111     GTEST_SKIP() << "statx returned ENOSYS";
112   }
113   ASSERT_EQ(0, rc);
114   struct stat64 sb;
115   ASSERT_EQ(0, stat64("/proc/version", &sb));
116   EXPECT_EQ(sb.st_ino, sx.stx_ino);
117   EXPECT_EQ(sb.st_mode, sx.stx_mode);
118 #else
119   GTEST_SKIP() << "statx not available";
120 #endif
121 }
122 
TEST(sys_stat,fchmodat_EFAULT_file)123 TEST(sys_stat, fchmodat_EFAULT_file) {
124   ASSERT_EQ(-1, fchmodat(AT_FDCWD, (char *) 0x1, 0751, 0));
125   ASSERT_EQ(EFAULT, errno);
126 }
127 
TEST(sys_stat,fchmodat_AT_SYMLINK_NOFOLLOW_EFAULT_file)128 TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_EFAULT_file) {
129   ASSERT_EQ(-1, fchmodat(AT_FDCWD, (char *) 0x1, 0751, AT_SYMLINK_NOFOLLOW));
130 #if defined(__BIONIC__)
131   ASSERT_EQ(EFAULT, errno);
132 #else
133   // glibc 2.19 does not implement AT_SYMLINK_NOFOLLOW and always
134   // returns ENOTSUP
135   ASSERT_EQ(ENOTSUP, errno);
136 #endif
137 }
138 
TEST(sys_stat,fchmodat_bad_flags)139 TEST(sys_stat, fchmodat_bad_flags) {
140   ASSERT_EQ(-1, fchmodat(AT_FDCWD, "/blah", 0751, ~AT_SYMLINK_NOFOLLOW));
141   ASSERT_EQ(EINVAL, errno);
142 }
143 
TEST(sys_stat,fchmodat_bad_flags_ALL)144 TEST(sys_stat, fchmodat_bad_flags_ALL) {
145   ASSERT_EQ(-1, fchmodat(AT_FDCWD, "/blah", 0751, ~0));
146   ASSERT_EQ(EINVAL, errno);
147 }
148 
TEST(sys_stat,fchmodat_nonexistant_file)149 TEST(sys_stat, fchmodat_nonexistant_file) {
150   ASSERT_EQ(-1, fchmodat(AT_FDCWD, "/blah", 0751, 0));
151   ASSERT_EQ(ENOENT, errno);
152 }
153 
TEST(sys_stat,fchmodat_AT_SYMLINK_NOFOLLOW_nonexistant_file)154 TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_nonexistant_file) {
155   ASSERT_EQ(-1, fchmodat(AT_FDCWD, "/blah", 0751, AT_SYMLINK_NOFOLLOW));
156 #if defined(__BIONIC__)
157   ASSERT_EQ(ENOENT, errno);
158 #else
159   // glibc 2.19 does not implement AT_SYMLINK_NOFOLLOW and always
160   // returns ENOTSUP
161   ASSERT_EQ(ENOTSUP, errno);
162 #endif
163 }
164 
AssertFileModeEquals(mode_t expected_mode,const char * filename)165 static void AssertFileModeEquals(mode_t expected_mode, const char* filename) {
166   struct stat sb;
167   ASSERT_EQ(0, stat(filename, &sb));
168   mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO;
169   ASSERT_EQ(expected_mode & mask, static_cast<mode_t>(sb.st_mode) & mask);
170 }
171 
TEST(sys_stat,fchmodat_file)172 TEST(sys_stat, fchmodat_file) {
173   TemporaryFile tf;
174 
175   ASSERT_EQ(0, fchmodat(AT_FDCWD, tf.path, 0751, 0));
176   AssertFileModeEquals(0751, tf.path);
177 }
178 
TEST(sys_stat,fchmodat_AT_SYMLINK_NOFOLLOW_file)179 TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_file) {
180   TemporaryFile tf;
181   errno = 0;
182   int result = fchmodat(AT_FDCWD, tf.path, 0751, AT_SYMLINK_NOFOLLOW);
183 
184 #if defined(__BIONIC__)
185   ASSERT_EQ(0, result);
186   ASSERT_EQ(0, errno);
187   AssertFileModeEquals(0751, tf.path);
188 #else
189   // glibc 2.19 does not implement AT_SYMLINK_NOFOLLOW and always
190   // returns ENOTSUP
191   ASSERT_EQ(-1, result);
192   ASSERT_EQ(ENOTSUP, errno);
193 #endif
194 }
195 
TEST(sys_stat,fchmodat_symlink)196 TEST(sys_stat, fchmodat_symlink) {
197   TemporaryFile tf;
198   char linkname[255];
199 
200   snprintf(linkname, sizeof(linkname), "%s.link", tf.path);
201 
202   ASSERT_EQ(0, symlink(tf.path, linkname));
203   ASSERT_EQ(0, fchmodat(AT_FDCWD, linkname, 0751, 0));
204   AssertFileModeEquals(0751, tf.path);
205   unlink(linkname);
206 }
207 
TEST(sys_stat,fchmodat_dangling_symlink)208 TEST(sys_stat, fchmodat_dangling_symlink) {
209   TemporaryFile tf;
210   char linkname[255];
211   char target[255];
212 
213   snprintf(linkname, sizeof(linkname), "%s.link", tf.path);
214   snprintf(target, sizeof(target), "%s.doesnotexist", tf.path);
215 
216   ASSERT_EQ(0, symlink(target, linkname));
217   ASSERT_EQ(-1, fchmodat(AT_FDCWD, linkname, 0751, 0));
218   ASSERT_EQ(ENOENT, errno);
219   unlink(linkname);
220 }
221 
AssertSymlinkModeEquals(mode_t expected_mode,const char * linkname)222 static void AssertSymlinkModeEquals(mode_t expected_mode, const char* linkname) {
223   struct stat sb;
224   ASSERT_EQ(0, fstatat(AT_FDCWD, linkname, &sb, AT_SYMLINK_NOFOLLOW));
225   mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO;
226   ASSERT_EQ(expected_mode & mask, static_cast<mode_t>(sb.st_mode) & mask);
227 }
228 
TEST(sys_stat,fchmodat_AT_SYMLINK_NOFOLLOW_with_symlink)229 TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_with_symlink) {
230   TemporaryFile tf;
231   struct stat tf_sb;
232   ASSERT_EQ(0, stat(tf.path, &tf_sb));
233 
234   char linkname[255];
235   snprintf(linkname, sizeof(linkname), "%s.link", tf.path);
236 
237   ASSERT_EQ(0, symlink(tf.path, linkname));
238   int result = fchmodat(AT_FDCWD, linkname, 0751, AT_SYMLINK_NOFOLLOW);
239   // It depends on the kernel whether chmod operation on symlink is allowed.
240   if (result == 0) {
241     AssertSymlinkModeEquals(0751, linkname);
242   } else {
243     ASSERT_EQ(-1, result);
244     ASSERT_EQ(ENOTSUP, errno);
245   }
246 
247   // Target file mode shouldn't be modified.
248   AssertFileModeEquals(tf_sb.st_mode, tf.path);
249   unlink(linkname);
250 }
251 
TEST(sys_stat,fchmodat_AT_SYMLINK_NOFOLLOW_with_dangling_symlink)252 TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_with_dangling_symlink) {
253   TemporaryFile tf;
254 
255   char linkname[255];
256   char target[255];
257   snprintf(linkname, sizeof(linkname), "%s.link", tf.path);
258   snprintf(target, sizeof(target), "%s.doesnotexist", tf.path);
259 
260   ASSERT_EQ(0, symlink(target, linkname));
261   int result = fchmodat(AT_FDCWD, linkname, 0751, AT_SYMLINK_NOFOLLOW);
262   // It depends on the kernel whether chmod operation on symlink is allowed.
263   if (result == 0) {
264     AssertSymlinkModeEquals(0751, linkname);
265   } else {
266     ASSERT_EQ(-1, result);
267     ASSERT_EQ(ENOTSUP, errno);
268   }
269 
270   unlink(linkname);
271 }
272 
TEST(sys_stat,faccessat_EINVAL)273 TEST(sys_stat, faccessat_EINVAL) {
274   ASSERT_EQ(-1, faccessat(AT_FDCWD, "/dev/null", F_OK, ~AT_SYMLINK_NOFOLLOW));
275   ASSERT_EQ(EINVAL, errno);
276 #if defined(__BIONIC__)
277   ASSERT_EQ(-1, faccessat(AT_FDCWD, "/dev/null", ~(R_OK | W_OK | X_OK), 0));
278   ASSERT_EQ(EINVAL, errno);
279 #else
280   ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", ~(R_OK | W_OK | X_OK), AT_SYMLINK_NOFOLLOW));
281   ASSERT_EQ(-1, faccessat(AT_FDCWD, "/dev/null", ~(R_OK | W_OK | X_OK), 0));
282   ASSERT_EQ(EINVAL, errno);
283 #endif
284 }
285 
TEST(sys_stat,faccessat_AT_SYMLINK_NOFOLLOW_EINVAL)286 TEST(sys_stat, faccessat_AT_SYMLINK_NOFOLLOW_EINVAL) {
287 #if defined(__BIONIC__)
288   // Android doesn't support AT_SYMLINK_NOFOLLOW
289   ASSERT_EQ(-1, faccessat(AT_FDCWD, "/dev/null", F_OK, AT_SYMLINK_NOFOLLOW));
290   ASSERT_EQ(EINVAL, errno);
291 #else
292   ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", F_OK, AT_SYMLINK_NOFOLLOW));
293 #endif
294 }
295 
TEST(sys_stat,faccessat_dev_null)296 TEST(sys_stat, faccessat_dev_null) {
297   ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", F_OK, 0));
298   ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", R_OK, 0));
299   ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", W_OK, 0));
300   ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", R_OK|W_OK, 0));
301 }
302 
TEST(sys_stat,faccessat_nonexistant)303 TEST(sys_stat, faccessat_nonexistant) {
304   ASSERT_EQ(-1, faccessat(AT_FDCWD, "/blah", F_OK, AT_SYMLINK_NOFOLLOW));
305 #if defined(__BIONIC__)
306   // Android doesn't support AT_SYMLINK_NOFOLLOW
307   ASSERT_EQ(EINVAL, errno);
308 #else
309   ASSERT_EQ(ENOENT, errno);
310 #endif
311 }
312