1*4b9c6d91SCole Faust /* unittest_util.h
2*4b9c6d91SCole Faust * Copyright 2022 The ChromiumOS Authors
3*4b9c6d91SCole Faust * Use of this source code is governed by a BSD-style license that can be
4*4b9c6d91SCole Faust * found in the LICENSE file.
5*4b9c6d91SCole Faust *
6*4b9c6d91SCole Faust * Utility functions for unit tests.
7*4b9c6d91SCole Faust */
8*4b9c6d91SCole Faust
9*4b9c6d91SCole Faust #include <errno.h>
10*4b9c6d91SCole Faust #include <ftw.h>
11*4b9c6d91SCole Faust #include <stdio.h>
12*4b9c6d91SCole Faust #include <stdlib.h>
13*4b9c6d91SCole Faust #include <unistd.h>
14*4b9c6d91SCole Faust
15*4b9c6d91SCole Faust #include "util.h"
16*4b9c6d91SCole Faust
17*4b9c6d91SCole Faust namespace {
18*4b9c6d91SCole Faust
is_android_constexpr()19*4b9c6d91SCole Faust constexpr bool is_android_constexpr() {
20*4b9c6d91SCole Faust #if defined(__ANDROID__)
21*4b9c6d91SCole Faust return true;
22*4b9c6d91SCole Faust #else
23*4b9c6d91SCole Faust return false;
24*4b9c6d91SCole Faust #endif
25*4b9c6d91SCole Faust }
26*4b9c6d91SCole Faust
27*4b9c6d91SCole Faust // Returns a template path that can be used as an argument to mkstemp / mkdtemp.
temp_path_pattern()28*4b9c6d91SCole Faust constexpr const char* temp_path_pattern() {
29*4b9c6d91SCole Faust if (is_android_constexpr())
30*4b9c6d91SCole Faust return "/data/local/tmp/minijail.tests.XXXXXX";
31*4b9c6d91SCole Faust else
32*4b9c6d91SCole Faust return "minijail.tests.XXXXXX";
33*4b9c6d91SCole Faust }
34*4b9c6d91SCole Faust
35*4b9c6d91SCole Faust // Recursively deletes the subtree rooted at |path|.
rmdir_recursive(const std::string & path)36*4b9c6d91SCole Faust bool rmdir_recursive(const std::string& path) {
37*4b9c6d91SCole Faust auto callback = [](const char* child, const struct stat*, int file_type,
38*4b9c6d91SCole Faust struct FTW*) -> int {
39*4b9c6d91SCole Faust if (file_type == FTW_DP) {
40*4b9c6d91SCole Faust if (rmdir(child) == -1) {
41*4b9c6d91SCole Faust fprintf(stderr, "rmdir(%s): %s\n", child, strerror(errno));
42*4b9c6d91SCole Faust return -1;
43*4b9c6d91SCole Faust }
44*4b9c6d91SCole Faust } else if (file_type == FTW_F) {
45*4b9c6d91SCole Faust if (unlink(child) == -1) {
46*4b9c6d91SCole Faust fprintf(stderr, "unlink(%s): %s\n", child, strerror(errno));
47*4b9c6d91SCole Faust return -1;
48*4b9c6d91SCole Faust }
49*4b9c6d91SCole Faust }
50*4b9c6d91SCole Faust return 0;
51*4b9c6d91SCole Faust };
52*4b9c6d91SCole Faust
53*4b9c6d91SCole Faust return nftw(path.c_str(), callback, 128, FTW_DEPTH) == 0;
54*4b9c6d91SCole Faust }
55*4b9c6d91SCole Faust
56*4b9c6d91SCole Faust } // namespace
57*4b9c6d91SCole Faust
58*4b9c6d91SCole Faust // Creates a temporary directory that will be cleaned up upon leaving scope.
59*4b9c6d91SCole Faust class TemporaryDir {
60*4b9c6d91SCole Faust public:
TemporaryDir()61*4b9c6d91SCole Faust TemporaryDir() : path(temp_path_pattern()) {
62*4b9c6d91SCole Faust if (mkdtemp(const_cast<char*>(path.c_str())) == nullptr)
63*4b9c6d91SCole Faust path.clear();
64*4b9c6d91SCole Faust }
~TemporaryDir()65*4b9c6d91SCole Faust ~TemporaryDir() {
66*4b9c6d91SCole Faust if (!is_valid())
67*4b9c6d91SCole Faust return;
68*4b9c6d91SCole Faust rmdir_recursive(path.c_str());
69*4b9c6d91SCole Faust }
70*4b9c6d91SCole Faust
is_valid()71*4b9c6d91SCole Faust bool is_valid() const { return !path.empty(); }
72*4b9c6d91SCole Faust
73*4b9c6d91SCole Faust std::string path;
74*4b9c6d91SCole Faust
75*4b9c6d91SCole Faust private:
76*4b9c6d91SCole Faust TemporaryDir(const TemporaryDir&) = delete;
77*4b9c6d91SCole Faust TemporaryDir& operator=(const TemporaryDir&) = delete;
78*4b9c6d91SCole Faust };
79*4b9c6d91SCole Faust
80*4b9c6d91SCole Faust // Creates a named temporary file that will be cleaned up upon leaving scope.
81*4b9c6d91SCole Faust class TemporaryFile {
82*4b9c6d91SCole Faust public:
TemporaryFile()83*4b9c6d91SCole Faust TemporaryFile() : path(temp_path_pattern()) {
84*4b9c6d91SCole Faust int fd = mkstemp(const_cast<char*>(path.c_str()));
85*4b9c6d91SCole Faust if (fd == -1) {
86*4b9c6d91SCole Faust path.clear();
87*4b9c6d91SCole Faust return;
88*4b9c6d91SCole Faust }
89*4b9c6d91SCole Faust close(fd);
90*4b9c6d91SCole Faust }
~TemporaryFile()91*4b9c6d91SCole Faust ~TemporaryFile() {
92*4b9c6d91SCole Faust if (!is_valid())
93*4b9c6d91SCole Faust return;
94*4b9c6d91SCole Faust unlink(path.c_str());
95*4b9c6d91SCole Faust }
96*4b9c6d91SCole Faust
is_valid()97*4b9c6d91SCole Faust bool is_valid() const { return !path.empty(); }
98*4b9c6d91SCole Faust
99*4b9c6d91SCole Faust std::string path;
100*4b9c6d91SCole Faust
101*4b9c6d91SCole Faust private:
102*4b9c6d91SCole Faust TemporaryFile(const TemporaryFile&) = delete;
103*4b9c6d91SCole Faust TemporaryFile& operator=(const TemporaryFile&) = delete;
104*4b9c6d91SCole Faust };
105