1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <lib/devmgr-integration-test/fixture.h>
6 
7 #include <fcntl.h>
8 
9 #include <fbl/unique_fd.h>
10 #include <lib/fdio/watcher.h>
11 
12 namespace devmgr_integration_test {
13 
14 // Waits for |file| to appear in |dir|, and opens it when it does.  Times out if
15 // the deadline passes.
WaitForFile(const fbl::unique_fd & dir,const char * file,zx::time deadline,fbl::unique_fd * out)16 zx_status_t WaitForFile(const fbl::unique_fd& dir, const char* file, zx::time deadline,
17                         fbl::unique_fd* out) {
18     auto watch_func = [](int dirfd, int event, const char* fn, void* cookie) -> zx_status_t {
19         auto file = reinterpret_cast<const char*>(cookie);
20         if (event != WATCH_EVENT_ADD_FILE) {
21             return ZX_OK;
22         }
23         if (!strcmp(fn, file)) {
24             return ZX_ERR_STOP;
25         }
26         return ZX_OK;
27     };
28 
29     zx_status_t status = fdio_watch_directory(dir.get(), watch_func, deadline.get(),
30                                               const_cast<char*>(file));
31     if (status != ZX_ERR_STOP) {
32         return status;
33     }
34     out->reset(openat(dir.get(), file, O_RDWR));
35     if (!out->is_valid()) {
36         return ZX_ERR_IO;
37     }
38     return ZX_OK;
39 }
40 
41 namespace {
42 
43 // Version of recursive_wait_for_file that can mutate its path
RecursiveWaitForFileHelper(const fbl::unique_fd & dir,char * path,zx::time deadline,fbl::unique_fd * out)44 zx_status_t RecursiveWaitForFileHelper(const fbl::unique_fd& dir, char* path,
45                                        zx::time deadline, fbl::unique_fd* out) {
46     char* first_slash = strchr(path, '/');
47     if (first_slash == nullptr) {
48         // If there's no first slash, then we're just waiting for the file
49         // itself to appear.
50         return WaitForFile(dir, path, deadline, out);
51     }
52     *first_slash = 0;
53 
54     fbl::unique_fd next_dir;
55     zx_status_t status = WaitForFile(dir, path, deadline, &next_dir);
56     if (status != ZX_OK) {
57         return status;
58     }
59     return RecursiveWaitForFileHelper(next_dir, first_slash + 1, deadline, out);
60 }
61 
62 } // namespace
63 
64 // Waits for the relative |path| starting in |dir| to appear, and opens it.
RecursiveWaitForFile(const fbl::unique_fd & dir,const char * path,zx::time deadline,fbl::unique_fd * out)65 zx_status_t RecursiveWaitForFile(const fbl::unique_fd& dir, const char* path,
66                                  zx::time deadline, fbl::unique_fd* out) {
67     char path_copy[PATH_MAX];
68     if (strlen(path) >= sizeof(path_copy)) {
69         return ZX_ERR_INVALID_ARGS;
70     }
71     strcpy(path_copy, path);
72     return RecursiveWaitForFileHelper(dir, path_copy, deadline, out);
73 }
74 
75 } // namespace devmgr_integration_test
76