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