1 /*
2  * Copyright (C) 2014 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 <dirent.h>
20 #include <dlfcn.h>
21 #include <pthread.h>
22 #include <sys/file.h>
23 #include <sys/select.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 
27 #include <cerrno>
28 #include <cstdarg>
29 #include <cstdio>
30 #include <cstdlib>
31 
32 #include "file.h"  // NOLINT
33 
34 //------------------------------------------------------------------------------
35 // Test simple file IO
36 
TEST(File,Mkstemp)37 TEST(File, Mkstemp) {
38   char* temp = strdup(TempFileTemplate());
39   int fd = mkstemp(temp);
40   ASSERT_NE(fd, -1);
41   ASSERT_EQ(close(fd), 0);
42   ASSERT_EQ(unlink(temp), 0);
43   free(temp);
44 }
45 
46 extern "C" int mkstemps(char* templ, int suffix_len);
47 
TEST(File,Mkstemps)48 TEST(File, Mkstemps) {
49   char* temp;
50   asprintf(&temp, "%s%s", TempFileTemplate(), ".txt");
51   int fd = mkstemps(temp, 4);
52   ASSERT_NE(fd, -1);
53   ASSERT_EQ(access(temp, R_OK | W_OK), 0);
54   ASSERT_EQ(close(fd), 0);
55   ASSERT_EQ(unlink(temp), 0);
56   free(temp);
57 }
58 
TEST(File,Fdopen)59 TEST(File, Fdopen) {
60   TempFile f;
61   ASSERT_TRUE(f.get());
62 }
63 
TEST(File,ReadWrite)64 TEST(File, ReadWrite) {
65   TempFile f;
66   ASSERT_EQ(fwrite("Hello", 1, 5, f.get()), 5U);
67   ASSERT_EQ(fseek(f.get(), 0, SEEK_SET), 0);
68   char buf[5];
69   ASSERT_EQ(fread(&buf, 1, 5, f.get()), 5U);
70   EXPECT_EQ(buf[0], 'H');
71   EXPECT_EQ(buf[4], 'o');
72 }
73 
TEST(File,PReadWrite)74 TEST(File, PReadWrite) {
75   TempFile f;
76   const char* hello = "Hello";
77   ASSERT_EQ(pwrite(f.fd(), hello, strlen(hello), 0), 5);
78   char buf[5];
79   ASSERT_EQ(pread(f.fd(), buf, 4, 1), 4);
80   buf[4] = '\0';
81   EXPECT_STREQ(buf, "ello");
82 }
83 
TEST(File,fileno)84 TEST(File, fileno) {
85   TempFile f;
86   ASSERT_EQ(f.fd(), fileno(f.get()));
87 }
88 
TEST(File,Ftell)89 TEST(File, Ftell) {
90   TempFile f;
91   EXPECT_EQ(ftell(f.get()), 0);
92   ASSERT_EQ(fwrite("Hello", 1, 5, f.get()), 5U);
93   EXPECT_EQ(ftell(f.get()), 5);
94   ASSERT_EQ(fseek(f.get(), 1, SEEK_SET), 0);
95   EXPECT_EQ(ftell(f.get()), 1);
96 }
97 
TEST(File,Lseek)98 TEST(File, Lseek) {
99   TempFile f;
100   ASSERT_EQ(fwrite("Hello", 1, 5, f.get()), 5U);
101   ASSERT_EQ(fflush(f.get()), 0);
102   EXPECT_EQ(lseek(f.fd(), 1, SEEK_SET), 1);
103   EXPECT_EQ(lseek(f.fd(), 1, SEEK_CUR), 2);
104   EXPECT_EQ(lseek(f.fd(), -1, SEEK_END), 4);
105   EXPECT_EQ(lseek64(f.fd(), -2, SEEK_END), 3);
106 }
107 
TEST(File,Ftruncate)108 TEST(File, Ftruncate) {
109   TempFile f;
110   ASSERT_EQ(fwrite("Hello", 1, 5, f.get()), 5U);
111   ASSERT_EQ(fflush(f.get()), 0);
112   ASSERT_EQ(lseek(f.fd(), 0, SEEK_END), 5);
113   ASSERT_EQ(lseek(f.fd(), 0, SEEK_SET), 0);
114   EXPECT_EQ(ftruncate(f.fd(), -1), -1);
115   EXPECT_EQ(ftruncate(f.fd(), 3), 0);
116   EXPECT_EQ(lseek(f.fd(), 0, SEEK_END), 3);
117 }
118 
TEST(File,Reopen)119 TEST(File, Reopen) {
120   TempFile f;
121   // freopen(nullptr, ...) is not supported in bionic.
122   ASSERT_TRUE(freopen(f.FileName(), "r", f.get()));
123   EXPECT_EQ(fwrite("Hello", 1, 5, f.get()), 0U);
124   ASSERT_TRUE(freopen(f.FileName(), "r+", f.get()));
125   EXPECT_EQ(fwrite("Hello", 1, 5, f.get()), 5U);
126 }
127 
TEST(File,ODirectoryFlag)128 TEST(File, ODirectoryFlag) {
129   TempFile f;
130   errno = 0;
131   // Tries to open a file with O_DIRECTORY, which should fail.
132   ASSERT_EQ(open(f.FileName(), O_RDONLY | O_DIRECTORY), -1);
133   EXPECT_EQ(errno, ENOTDIR);
134 }
135 
TEST(File,TempFile)136 TEST(File, TempFile) {
137   FILE* f = tmpfile();
138   ASSERT_TRUE(f);
139   ASSERT_EQ(fclose(f), 0);
140 }
141 
TestStatBuf(const struct stat & buf,int file_size,const char * msg)142 void TestStatBuf(const struct stat& buf, int file_size, const char* msg) {
143   EXPECT_EQ(buf.st_size, file_size) << msg;
144   EXPECT_EQ(buf.st_nlink, 1U) << msg;
145   // regular file with chmod 600.
146   EXPECT_EQ(static_cast<int>(buf.st_mode), S_IFREG | S_IRUSR | S_IWUSR) << msg;
147   EXPECT_NE(buf.st_blksize, 0U) << msg;
148   EXPECT_EQ(buf.st_blksize % 512, 0U) << msg;
149   EXPECT_NE(buf.st_mtime, 0) << msg;
150   // We do not support st_atime/st_ctime.
151 }
152 
TEST(File,Stat)153 TEST(File, Stat) {
154   // Make sure file will have the exact permissions we want.
155   mode_t saved_umask = umask(S_IRWXG | S_IRWXO);
156   TempFile f;
157   ASSERT_GE(fputs("test", f.get()), 0);
158   ASSERT_EQ(fflush(f.get()), 0);
159   struct stat buf;
160   ASSERT_EQ(stat(f.FileName(), &buf), 0);
161   TestStatBuf(buf, 4, "stat");
162   ASSERT_EQ(lstat(f.FileName(), &buf), 0);
163   TestStatBuf(buf, 4, "lstat");
164   ASSERT_EQ(fstat(f.fd(), &buf), 0);
165   TestStatBuf(buf, 4, "fstat");
166   // Restore umask.
167   umask(saved_umask);
168 }
169 
vfprintf_call(FILE * f,const char * format,...)170 int vfprintf_call(FILE* f, const char* format, ...) {
171   va_list args;
172   va_start(args, format);
173   int result = vfprintf(f, format, args);
174   va_end(args);
175   return result;
176 }
177 
vfscanf_call(FILE * f,const char * format,...)178 int vfscanf_call(FILE* f, const char* format, ...) {
179   va_list args;
180   va_start(args, format);
181   int result = vfscanf(f, format, args);
182   va_end(args);
183   return result;
184 }
185 
TEST(File,PrintfScanf)186 TEST(File, PrintfScanf) {
187   TempFile f;
188   ASSERT_GT(fprintf(f.get(), "%d %lf %lld %p\n", 1, 2.0, 3LL, &f), 0);
189   ASSERT_EQ(fseek(f.get(), 0, SEEK_SET), 0);
190   int res_int;
191   double res_double;
192   int64_t res_long;
193   TempFile* res_pointer;
194   ASSERT_EQ(fscanf(f.get(), "%d%lf%lld%p", &res_int, &res_double, &res_long, &res_pointer), 4);
195   EXPECT_EQ(res_int, 1);
196   EXPECT_DOUBLE_EQ(res_double, 2.0);
197   EXPECT_EQ(res_long, 3);
198   EXPECT_EQ(res_pointer, &f);
199   ASSERT_EQ(fseek(f.get(), 0, SEEK_SET), 0);
200 
201   ASSERT_EQ(vfscanf_call(f.get(), "%d%lf%lld%p", &res_int, &res_double, &res_long, &res_pointer),
202             4);
203   EXPECT_EQ(res_int, 1);
204   EXPECT_DOUBLE_EQ(res_double, 2.0);
205   EXPECT_EQ(res_long, 3);
206   EXPECT_EQ(res_pointer, &f);
207   ASSERT_EQ(fseek(f.get(), 0, SEEK_SET), 0);
208 
209   ASSERT_GT(vfprintf_call(f.get(), "%.1lf_%d_%lld\n", 1.0, 2, 3LL), 0);
210   ASSERT_EQ(fseek(f.get(), 0, SEEK_SET), 0);
211   char data[256];
212   ASSERT_EQ(fscanf(f.get(), "%s", data), 1);
213   EXPECT_STREQ(data, "1.0_2_3");
214   const char* in_str = "http://foo.bar.com/main?lang=US";
215   char more_data[64];
216   ASSERT_EQ(sscanf(in_str, "%15[^:]:%[^\n]", data, more_data), 2);  // NOLINT
217   EXPECT_STREQ(data, "http");
218   EXPECT_STREQ(more_data, "//foo.bar.com/main?lang=US");
219   ASSERT_EQ(fseek(f.get(), 0, SEEK_SET), 0);
220   ASSERT_GT(fprintf(f.get(), "%0*d\n", 2, 1), 0);
221   ASSERT_EQ(fseek(f.get(), 0, SEEK_SET), 0);
222   ASSERT_EQ(fscanf(f.get(), "%s", data), 1);
223   EXPECT_STREQ(data, "01");
224   ASSERT_EQ(fseek(f.get(), 0, SEEK_SET), 0);
225 }
226 
TEST(File,PositionalPrintf)227 TEST(File, PositionalPrintf) {
228   TempFile f;
229   char buf[256];
230 #define CHECK_PRINTF(result, format, ...)                     \
231   do {                                                        \
232     ASSERT_EQ(fseek(f.get(), 0, SEEK_SET), 0);                \
233     ASSERT_GT(fprintf(f.get(), format "\n", __VA_ARGS__), 0); \
234     ASSERT_EQ(fseek(f.get(), 0, SEEK_SET), 0);                \
235     ASSERT_EQ(fgets(buf, sizeof(buf), f.get()), buf);         \
236     EXPECT_STREQ(buf, result "\n");                           \
237   } while (0)
238 
239   // Good
240   CHECK_PRINTF("2 3.0", "%1$d %2$.1lf", 2, 3.0);
241   CHECK_PRINTF("2 3.0", "%2$d %1$.1lf", 3.0, 2);
242   CHECK_PRINTF("2.000", "%2$.*1$lf", 3, 2.0);
243   CHECK_PRINTF(" abc", "%2$*1$s", 4, "abc");
244   CHECK_PRINTF("1 1 2", "%1$d %1$d %2$d", 1, 2);
245 
246 #pragma GCC diagnostic push
247 #pragma GCC diagnostic ignored "-Wformat"
248   // Bad
249   CHECK_PRINTF("2 3 1", "%2$d %d %1$d", 1, 2, 3);
250   CHECK_PRINTF(" abc 4 1", "%1$*2$s %d %3$d", "abc", 4, 1);
251 
252   // Ugly
253   CHECK_PRINTF(" abc 1", "%1$*s %d", 4, "abc", 1);
254   CHECK_PRINTF("1 2 2", "%d %d %1$2$d", 1, 2);
255 #pragma GCC diagnostic pop
256 
257 #undef CHECK_PRINTF
258 }
259 
TEST(File,FdPrintf)260 TEST(File, FdPrintf) {
261   TempFile f;
262   using FdPrintf = int (*)(int fd, const char* format, ...);
263   FdPrintf fdprintf = reinterpret_cast<FdPrintf>(dlsym(RTLD_DEFAULT, "fdprintf"));
264   ASSERT_GT(fdprintf(f.fd(), "%.1lf %d %lld\n", 1.0, 2, 3LL), 0);
265   ASSERT_EQ(fseek(f.get(), 0, SEEK_SET), 0);
266   char buf[16];
267   ASSERT_EQ(fgets(buf, sizeof(buf), f.get()), buf);
268   EXPECT_STREQ(buf, "1.0 2 3\n");
269 }
270 
TEST(File,GetPut)271 TEST(File, GetPut) {
272   TempFile f;
273   ASSERT_GE(fputs("Hell", f.get()), 0);
274   ASSERT_EQ(fputc('o', f.get()), 'o');
275   ASSERT_EQ(fseek(f.get(), 0, SEEK_SET), 0);
276   EXPECT_EQ(fgetc(f.get()), 'H');
277   ASSERT_EQ(fseek(f.get(), 0, SEEK_SET), 0);
278   char buf[6];
279   ASSERT_EQ(buf, fgets(buf, sizeof(buf), f.get()));
280   EXPECT_EQ(buf[0], 'H');
281   EXPECT_EQ(buf[4], 'o');
282 }
283 
TEST(File,Feof)284 TEST(File, Feof) {
285   TempFile f;
286   ASSERT_EQ(fgetc(f.get()), EOF);
287   ASSERT_TRUE(feof(f.get()));
288   clearerr(f.get());
289   ASSERT_FALSE(feof(f.get()));
290 }
291 
TEST(File,Ungetc)292 TEST(File, Ungetc) {
293   TempFile f;
294   ASSERT_EQ(ungetc(' ', f.get()), ' ');
295   ASSERT_EQ(fgetc(f.get()), ' ');
296   ASSERT_EQ(fgetc(f.get()), EOF);
297 }
298 
TEST(File,Setvbuf)299 TEST(File, Setvbuf) {
300   char buf[1024];
301   memset(buf, 1, sizeof(buf));
302   TempFile f;
303   EXPECT_EQ(setvbuf(f.get(), nullptr, _IOFBF, 1024), 0);
304   ASSERT_EQ(setvbuf(f.get(), buf, _IOFBF, sizeof(buf)), 0);
305   char data[2048];
306   memset(data, 2, sizeof(data));
307   ASSERT_EQ(fwrite(data, 1, 1, f.get()), 1U);
308   // Check that buffer is used
309   EXPECT_TRUE(memchr(buf, 2, sizeof(buf)));
310   // Check that it doesn't corrupt read/writes.
311   ASSERT_EQ(fwrite(data, 1, sizeof(data), f.get()), sizeof(data));
312   ASSERT_EQ(fseek(f.get(), 0, SEEK_SET), 0);
313   char rdata[2048];
314   ASSERT_EQ(fread(rdata, 1, sizeof(rdata), f.get()), sizeof(rdata));
315   EXPECT_EQ(memcmp(data, rdata, sizeof(data)), 0);
316 }
317 
TEST(File,SetBuffer)318 TEST(File, SetBuffer) {
319   char buf[1024];
320   memset(buf, 1, sizeof(buf));
321   TempFile f;
322   setbuffer(f.get(), buf, sizeof(buf));
323   char data = 2;
324   ASSERT_EQ(fwrite(&data, 1, 1, f.get()), 1U);
325   ASSERT_TRUE(memchr(buf, 2, sizeof(buf)));
326 }
327 
vsprintf_call(char * str,const char * format,...)328 int vsprintf_call(char* str, const char* format, ...) {
329   va_list args;
330   va_start(args, format);
331   int result = vsprintf(str, format, args);
332   va_end(args);
333   return result;
334 }
335 
vsnprintf_call(char * str,size_t n,const char * format,...)336 int vsnprintf_call(char* str, size_t n, const char* format, ...) {
337   va_list args;
338   va_start(args, format);
339   int result = vsnprintf(str, n, format, args);
340   va_end(args);
341   return result;
342 }
343 
vasprintf_call(char ** str,const char * format,...)344 int vasprintf_call(char** str, const char* format, ...) {
345   va_list args;
346   va_start(args, format);
347   int result = vasprintf(str, format, args);
348   va_end(args);
349   return result;
350 }
351 
TEST(File,StringPrintfScanf)352 TEST(File, StringPrintfScanf) {
353   char data[256];
354   ASSERT_GT(snprintf(data, sizeof(data), "%d %lf %lld %p", 1, 2.0, 3LL, data), 0);
355   int res_int;
356   double res_double;
357   int64_t res_int64;
358   void* res_pointer;
359   ASSERT_EQ(sscanf(data,
360                    "%d%lf%lld%p",  // NOLINT
361                    &res_int,
362                    &res_double,
363                    &res_int64,
364                    &res_pointer),
365             4);
366   EXPECT_EQ(res_int, 1);
367   EXPECT_DOUBLE_EQ(res_double, 2.0);
368   EXPECT_EQ(res_int64, 3);
369   EXPECT_EQ(res_pointer, data);
370   ASSERT_GT(sprintf(data, "%.1lf %d %lld", 1.0, 2, 3LL), 0);  // NOLINT
371   EXPECT_STREQ(data, "1.0 2 3");
372   ASSERT_GT(sprintf(data, "%%%c", 'd'), 0);  // NOLINT
373   EXPECT_STREQ(data, "%d");
374   res_int64 = 3;
375   ASSERT_GT(sprintf(data,
376                     "%d %ld %lld %qd %d",  // NOLINT
377                     1,
378                     (long)2,
379                     res_int64,
380                     res_int64 + 1,
381                     5),
382             0);  // NOLINT
383   EXPECT_STREQ(data, "1 2 3 4 5");
384   ASSERT_GT(sprintf(data, "%s", "one two"), 0);  // NOLINT
385   EXPECT_STREQ(data, "one two");
386   char* new_data = nullptr;
387   ASSERT_GT(asprintf(&new_data, "%.1lf %d %lld", 1.0, 2, 3LL), 0);  // NOLINT
388   ASSERT_NE(new_data, nullptr);
389   EXPECT_STREQ(new_data, "1.0 2 3");
390   free(new_data);
391   char word[256];
392   ASSERT_EQ(sscanf(data, "%s", word), 1);  // NOLINT
393   EXPECT_STREQ(word, "one");
394   ASSERT_GT(vsprintf_call(data, "%.1lf %d %lld", 1.0, 2, 3LL), 0);
395   EXPECT_STREQ(data, "1.0 2 3");
396   ASSERT_GT(vsnprintf_call(data, 256, "%.1lf %d %lld", 1.0, 2, 3LL), 0);
397   EXPECT_STREQ(data, "1.0 2 3");
398   new_data = nullptr;
399   ASSERT_GT(vasprintf_call(&new_data, "%.1lf %d %lld", 1.0, 2, 3LL), 0);
400   ASSERT_NE(new_data, nullptr);
401   EXPECT_STREQ(new_data, "1.0 2 3");
402   free(new_data);
403 }
404 
TEST(File,Select)405 TEST(File, Select) {
406   fd_set read;
407   struct timeval timeout;
408   FD_ZERO(&read);
409   FD_SET(STDOUT_FILENO, &read);
410   timeout.tv_sec = 0;
411   timeout.tv_usec = 1;
412   ASSERT_EQ(select(STDOUT_FILENO + 1, &read, nullptr, nullptr, &timeout), 0);
413   fd_set write;
414   FD_ZERO(&write);
415   FD_SET(STDOUT_FILENO, &write);
416   ASSERT_EQ(select(STDOUT_FILENO + 1, nullptr, &write, nullptr, nullptr), 1);
417 }
418 
ThreadPipeReadFunc(void * arg)419 void* ThreadPipeReadFunc(void* arg) {
420   int* iarg = reinterpret_cast<int*>(arg);
421   int fd = iarg[0];
422   char buf;
423   for (int i = 0; i < 1000; i++) {
424     if (read(fd, &buf, 1) != 1) {
425       return nullptr;
426     }
427   }
428   iarg[1] = 1;
429   return nullptr;
430 }
431 
TEST(File,Pipe)432 TEST(File, Pipe) {
433   int fds[2];
434   pthread_t thread;
435   int arg[2];
436   char buf = 0;
437   pipe(fds);
438   arg[0] = fds[0];
439   arg[1] = 0;
440   pthread_create(&thread, nullptr, ThreadPipeReadFunc, &arg);
441   for (int i = 0; i < 1000; i++) {
442     EXPECT_EQ(write(fds[1], &buf, 1), 1);
443   }
444   pthread_join(thread, nullptr);
445   EXPECT_EQ(arg[1], 1);
446 }
447 
448 // This function is implemented but is not present in public bionic headers.
449 extern "C" char* mkdtemp(char* tmpl);
450 
TEST(File,TempDir)451 TEST(File, TempDir) {
452   char* temp = strdup(TempFileTemplate());
453   // Currently mkdtemp can reuse existing directory because our mkdir is not
454   // POSIX compliant on Pepper FS.  See crbug.com/314879.
455   ASSERT_EQ(mkdtemp(temp), temp);
456   struct stat dir_stat;
457   ASSERT_EQ(stat(temp, &dir_stat), 0);
458   ASSERT_TRUE(S_ISDIR(dir_stat.st_mode));
459   ASSERT_EQ(rmdir(temp), 0);
460   free(temp);
461 }
462 
463 class TempDir {
464  public:
TempDir()465   TempDir() {
466     name_ = strdup(TempFileTemplate());
467     if (mkdtemp(name_) != name_) {
468       free(name_);
469       name_ = nullptr;
470     }
471   }
472 
TempDir(const char * dir)473   explicit TempDir(const char* dir) {
474     const char* kDirTemplate = "/ndk-tests-XXXXXX";
475     size_t max_len = strlen(dir) + strlen(kDirTemplate) + 1;
476     name_ = reinterpret_cast<char*>(malloc(max_len));
477     snprintf(name_, max_len, "%s%s", dir, kDirTemplate);
478     if (mkdtemp(name_) != name_) {
479       free(name_);
480       name_ = nullptr;
481     }
482   }
483 
~TempDir()484   ~TempDir() {
485     if (name_ != nullptr) {
486       rmdir(name_);
487       free(name_);
488     }
489   }
490 
GetDirName() const491   const char* GetDirName() const { return name_; }
492 
GetBaseName()493   const char* GetBaseName() {
494     const char* result = name_;
495     for (char* p = name_; *p; p++) {
496       if (*p == '/') {
497         result = p + 1;
498       }
499     }
500     return result;
501   }
502 
503  private:
504   char* name_;
505 };
506 
SkipDotDirsWithReaddir(DIR * pdir,struct dirent * entry)507 struct dirent* SkipDotDirsWithReaddir(DIR* pdir, struct dirent* entry) {
508   // skip "." and ".."
509   while (entry != nullptr && entry->d_name[0] == '.') {
510     entry = readdir(pdir);  // NOLINT(runtime/threadsafe_fn)
511   }
512   return entry;
513 }
514 
515 // We can't create files in /tmp directories outside of predefined places.
516 // We create directories inside of our temporary directory instead.
TEST(File,Readdir)517 TEST(File, Readdir) {
518   TempDir dir;
519   ASSERT_NE(dir.GetDirName(), nullptr);
520   TempDir dir1(dir.GetDirName());
521   ASSERT_NE(dir1.GetDirName(), nullptr);
522   TempDir dir2(dir.GetDirName());
523   ASSERT_NE(dir2.GetDirName(), nullptr);
524   DIR* pdir = opendir(dir.GetDirName());
525   ASSERT_NE(pdir, nullptr);
526   struct dirent* entry = readdir(pdir);  // NOLINT(runtime/threadsafe_fn)
527   entry = SkipDotDirsWithReaddir(pdir, entry);
528   ASSERT_NE(entry, nullptr);
529   EXPECT_EQ(entry->d_type, DT_DIR);
530   bool isDir1 = strcmp(dir1.GetBaseName(), entry->d_name) == 0;
531   bool isDir2 = strcmp(dir2.GetBaseName(), entry->d_name) == 0;
532   EXPECT_TRUE(isDir1 || isDir2);
533   entry = readdir(pdir);  // NOLINT(runtime/threadsafe_fn)
534   entry = SkipDotDirsWithReaddir(pdir, entry);
535   ASSERT_NE(entry, nullptr);
536   EXPECT_EQ(entry->d_type, DT_DIR);
537   isDir1 |= strcmp(dir1.GetBaseName(), entry->d_name) == 0;
538   isDir2 |= strcmp(dir2.GetBaseName(), entry->d_name) == 0;
539   EXPECT_TRUE(isDir1 && isDir2);
540   entry = readdir(pdir);  // NOLINT(runtime/threadsafe_fn)
541   entry = SkipDotDirsWithReaddir(pdir, entry);
542   ASSERT_EQ(entry, nullptr);
543   ASSERT_EQ(closedir(pdir), 0);
544 }
545 
546 extern "C" int getdents(unsigned int, dirent*, unsigned int) __attribute__((__weak__));
547 
TEST(File,Getdents)548 TEST(File, Getdents) {
549   TempDir dir;
550   ASSERT_NE(dir.GetDirName(), nullptr);
551   TempDir dir1(dir.GetDirName());
552   ASSERT_NE(dir1.GetDirName(), nullptr);
553   int dir_fd = open(dir.GetDirName(), O_RDONLY | O_DIRECTORY);
554   ASSERT_NE(dir_fd, -1);
555   char buf[1024];
556   int res = getdents(dir_fd, reinterpret_cast<struct dirent*>(buf), sizeof(buf));
557   ASSERT_GT(res, 0);
558   struct dirent* entry = reinterpret_cast<struct dirent*>(buf);
559   int entries = 0;
560   int pos = 0;
561   bool has_dir1 = false;
562   // Check that d_reclen is available first. d_type is the next field in the
563   // dirent structure.
564   while (static_cast<int>(offsetof(struct dirent, d_type)) <= res - pos &&
565          entry->d_reclen + pos <= res) {
566     entries++;
567     EXPECT_EQ(entry->d_type, DT_DIR);
568     if (strcmp(entry->d_name, dir1.GetBaseName()) == 0) {
569       has_dir1 = true;
570     }
571     pos += entry->d_reclen;
572     entry = reinterpret_cast<struct dirent*>(buf + pos);
573   }
574   EXPECT_EQ(entries, 3);
575   EXPECT_TRUE(has_dir1);
576   close(dir_fd);
577 }
578 
SkipDotDirsWithReaddir_r(DIR * pdir,struct dirent * entry,struct dirent ** result)579 bool SkipDotDirsWithReaddir_r(DIR* pdir, struct dirent* entry, struct dirent** result) {
580   // skip "." and ".."
581   while (*result != nullptr && entry->d_name[0] == '.') {
582     if (readdir_r(pdir, entry, result) != 0) {
583       return false;
584     }
585   }
586   return true;
587 }
588 
TestReaddirRWithDir(DIR * pdir,const char * innerdir)589 void TestReaddirRWithDir(DIR* pdir, const char* innerdir) {
590   struct dirent entry;
591   struct dirent* result;
592   ASSERT_EQ(readdir_r(pdir, &entry, &result), 0);
593   ASSERT_TRUE(SkipDotDirsWithReaddir_r(pdir, &entry, &result));
594   ASSERT_EQ(result, &entry);
595   EXPECT_EQ(entry.d_type, DT_DIR);
596   EXPECT_STREQ(entry.d_name, innerdir);
597   ASSERT_EQ(readdir_r(pdir, &entry, &result), 0);
598   ASSERT_TRUE(SkipDotDirsWithReaddir_r(pdir, &entry, &result));
599   EXPECT_EQ(result, nullptr);
600 }
601 
TEST(File,Readdir_r)602 TEST(File, Readdir_r) {
603   TempDir dir;
604   ASSERT_NE(dir.GetDirName(), nullptr);
605   TempDir dir1(dir.GetDirName());
606   ASSERT_NE(dir1.GetDirName(), nullptr);
607   DIR* pdir = opendir(dir.GetDirName());
608   ASSERT_NE(pdir, nullptr);
609   TestReaddirRWithDir(pdir, dir1.GetBaseName());
610   ASSERT_EQ(closedir(pdir), 0);
611 }
612 
TEST(File,Rewinddir)613 TEST(File, Rewinddir) {
614   TempDir dir;
615   ASSERT_NE(dir.GetDirName(), nullptr);
616   TempDir dir1(dir.GetDirName());
617   ASSERT_NE(dir1.GetDirName(), nullptr);
618   DIR* pdir = opendir(dir.GetDirName());
619   ASSERT_NE(pdir, nullptr);
620   TestReaddirRWithDir(pdir, dir1.GetBaseName());
621   rewinddir(pdir);
622   TestReaddirRWithDir(pdir, dir1.GetBaseName());
623   ASSERT_EQ(closedir(pdir), 0);
624 }
625 
ScandirFilter(const struct dirent * entry)626 int ScandirFilter(const struct dirent* entry) {
627   return entry->d_name[0] != '.';
628 }
629 
ScandirComparator(const struct dirent ** entry1,const struct dirent ** entry2)630 int ScandirComparator(const struct dirent** entry1, const struct dirent** entry2) {
631   return strcmp((*entry1)->d_name, (*entry2)->d_name);
632 }
633 
TEST(File,Scandir)634 TEST(File, Scandir) {
635   TempDir dir;
636   ASSERT_NE(dir.GetDirName(), nullptr);
637   TempDir dir1(dir.GetDirName());
638   ASSERT_NE(dir1.GetDirName(), nullptr);
639   TempDir dir2(dir.GetDirName());
640   ASSERT_NE(dir2.GetDirName(), nullptr);
641   struct dirent** namelist;
642   int size = scandir(dir.GetDirName(), &namelist, ScandirFilter, ScandirComparator);
643   ASSERT_EQ(size, 2);
644   ASSERT_LE(ScandirComparator(const_cast<const struct dirent**>(&namelist[0]),
645                               const_cast<const struct dirent**>(&namelist[1])),
646             0);
647   for (int i = 0; i < size; i++) {
648     free(namelist[i]);
649   }
650   free(namelist);
651 }
652 
TEST(File,FlockAlwaysSucceeds)653 TEST(File, FlockAlwaysSucceeds) {
654   TempFile f;
655   EXPECT_EQ(flock(f.fd(), LOCK_SH), 0);
656   EXPECT_EQ(flock(f.fd(), LOCK_EX), 0);
657   EXPECT_EQ(flock(f.fd(), LOCK_UN), 0);
658 }
659 
660 // Simple pseudo file.
661 struct funopen_cookie {
662   int pos;
663   char magic;
664 };
665 
funopen_read(void * cookie,char * data,int size)666 int funopen_read(void* cookie, char* data, int size) {
667   funopen_cookie* file = static_cast<funopen_cookie*>(cookie);
668   for (int i = 0; i < size; i++) {
669     data[i] = (file->pos + i) % 256;
670   }
671   file->pos += size;
672   return size;
673 }
674 
funopen_write(void * cookie,const char * data,int size)675 int funopen_write(void* cookie, const char* data, int size) {
676   funopen_cookie* file = static_cast<funopen_cookie*>(cookie);
677   for (int i = 0; i < size; i++) {
678     if (data[i] != file->magic) {
679       errno = EIO;
680       return 0;
681     }
682   }
683   file->pos += size;
684   return size;
685 }
686 
funopen_seek(void * cookie,fpos_t pos,int whence)687 fpos_t funopen_seek(void* cookie, fpos_t pos, int whence) {
688   funopen_cookie* file = static_cast<funopen_cookie*>(cookie);
689   switch (whence) {
690     case SEEK_SET:
691       file->pos = pos;
692       break;
693     case SEEK_CUR:
694       file->pos += pos;
695       break;
696     default:
697       errno = EINVAL;
698       return -1;
699   }
700   return file->pos;
701 }
702 
funopen_close(void *)703 int funopen_close(void* /* cookie */) {
704   return 0;
705 }
706 
TEST(File,Funopen)707 TEST(File, Funopen) {
708   funopen_cookie cookie = {0, 'a'};
709   FILE* f = funopen(&cookie, funopen_read, funopen_write, funopen_seek, funopen_close);
710   ASSERT_NE(f, nullptr);
711   // Disable buffering to make all file operations call funopen_* functions.
712   ASSERT_EQ(setvbuf(f, nullptr, _IONBF, 0), 0);
713   const size_t kBufSize = 4;
714   char buf[kBufSize];
715 
716   ASSERT_EQ(fread(buf, 1, kBufSize, f), kBufSize);
717   for (size_t i = 0; i < kBufSize; i++) {
718     EXPECT_EQ(static_cast<char>(i), buf[i]);
719   }
720   memset(buf, 'b', kBufSize);
721   ASSERT_EQ(fwrite(buf, 1, kBufSize, f), 0U);
722 
723   memset(buf, 'a', kBufSize);
724   ASSERT_EQ(fwrite(buf, 1, kBufSize, f), kBufSize);
725 
726   ASSERT_EQ(ftell(f), static_cast<int>(2 * kBufSize));
727   ASSERT_EQ(fseek(f, kBufSize, SEEK_SET), 0);
728   ASSERT_EQ(ftell(f), static_cast<int>(kBufSize));
729 
730   // No one can hear your scream in our pseudo file.
731   ASSERT_EQ(fread(buf, 1, kBufSize, f), kBufSize);
732   for (size_t i = 0; i < kBufSize; i++) {
733     EXPECT_EQ(buf[i], static_cast<char>(i + kBufSize));
734   }
735 
736   ASSERT_EQ(fclose(f), 0);
737 }
738 
TEST(File,UmaskActsSanely)739 TEST(File, UmaskActsSanely) {
740   mode_t saved_umask = umask(0600);
741   EXPECT_EQ(umask(0700), 0600);
742   EXPECT_EQ(umask(0600), 0700);
743   umask(saved_umask);
744 }
745