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 
TEST(sys_stat,futimens)26 TEST(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 
TEST(sys_stat,futimens_EBADF)48 TEST(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 
TEST(sys_stat,mkfifo_failure)58 TEST(sys_stat, mkfifo_failure) {
59   errno = 0;
60   ASSERT_EQ(-1, mkfifo("/", 0666));
61   ASSERT_EQ(EEXIST, errno);
62 }
63 
TEST(sys_stat,mkfifoat_failure)64 TEST(sys_stat, mkfifoat_failure) {
65   errno = 0;
66   ASSERT_EQ(-1, mkfifoat(-2, "x", 0666));
67   ASSERT_EQ(EBADF, errno);
68 }
69 
TEST(sys_stat,mkfifo)70 TEST(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 
TEST(sys_stat,stat64_lstat64_fstat64)90 TEST(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 
TEST(sys_stat,fchmodat_EFAULT_file)99 TEST(sys_stat, fchmodat_EFAULT_file) {
100   ASSERT_EQ(-1, fchmodat(AT_FDCWD, (char *) 0x1, 0751, 0));
101   ASSERT_EQ(EFAULT, errno);
102 }
103 
TEST(sys_stat,fchmodat_AT_SYMLINK_NOFOLLOW_EFAULT_file)104 TEST(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 
TEST(sys_stat,fchmodat_bad_flags)115 TEST(sys_stat, fchmodat_bad_flags) {
116   ASSERT_EQ(-1, fchmodat(AT_FDCWD, "/blah", 0751, ~AT_SYMLINK_NOFOLLOW));
117   ASSERT_EQ(EINVAL, errno);
118 }
119 
TEST(sys_stat,fchmodat_bad_flags_ALL)120 TEST(sys_stat, fchmodat_bad_flags_ALL) {
121   ASSERT_EQ(-1, fchmodat(AT_FDCWD, "/blah", 0751, ~0));
122   ASSERT_EQ(EINVAL, errno);
123 }
124 
TEST(sys_stat,fchmodat_nonexistant_file)125 TEST(sys_stat, fchmodat_nonexistant_file) {
126   ASSERT_EQ(-1, fchmodat(AT_FDCWD, "/blah", 0751, 0));
127   ASSERT_EQ(ENOENT, errno);
128 }
129 
TEST(sys_stat,fchmodat_AT_SYMLINK_NOFOLLOW_nonexistant_file)130 TEST(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 
AssertFileModeEquals(mode_t expected_mode,const char * filename)141 static 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 
TEST(sys_stat,fchmodat_file)148 TEST(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 
TEST(sys_stat,fchmodat_AT_SYMLINK_NOFOLLOW_file)155 TEST(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 
TEST(sys_stat,fchmodat_symlink)172 TEST(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 
TEST(sys_stat,fchmodat_dangling_symlink)184 TEST(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 
AssertSymlinkModeEquals(mode_t expected_mode,const char * linkname)198 static 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 
TEST(sys_stat,fchmodat_AT_SYMLINK_NOFOLLOW_with_symlink)205 TEST(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 
TEST(sys_stat,fchmodat_AT_SYMLINK_NOFOLLOW_with_dangling_symlink)228 TEST(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 
TEST(sys_stat,faccessat_EINVAL)249 TEST(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 
TEST(sys_stat,faccessat_AT_SYMLINK_NOFOLLOW_EINVAL)262 TEST(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 
TEST(sys_stat,faccessat_dev_null)272 TEST(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 
TEST(sys_stat,faccessat_nonexistant)279 TEST(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