1 /* 2 * Copyright (C) 2017 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 "perfetto/base/scoped_file.h" 18 19 #include <fcntl.h> 20 #include <unistd.h> 21 22 #include "gtest/gtest.h" 23 24 namespace perfetto { 25 namespace base { 26 namespace { 27 28 TEST(ScopedDirTest, CloseOutOfScope) { 29 DIR* dir_handle = opendir("."); 30 ASSERT_NE(nullptr, dir_handle); 31 int dir_handle_fd = dirfd(dir_handle); 32 ASSERT_GE(dir_handle_fd, 0); 33 { 34 ScopedDir scoped_dir(dir_handle); 35 ASSERT_EQ(dir_handle, scoped_dir.get()); 36 ASSERT_TRUE(scoped_dir); 37 } 38 ASSERT_NE(0, close(dir_handle_fd)); // Should fail when closing twice. 39 } 40 41 TEST(ScopedFileTest, CloseOutOfScope) { 42 int raw_fd = open("/dev/null", O_RDONLY); 43 ASSERT_GE(raw_fd, 0); 44 { 45 ScopedFile scoped_file(raw_fd); 46 ASSERT_EQ(raw_fd, scoped_file.get()); 47 ASSERT_EQ(raw_fd, *scoped_file); 48 ASSERT_TRUE(scoped_file); 49 } 50 ASSERT_NE(0, close(raw_fd)); // Should fail when closing twice. 51 } 52 53 TEST(ScopedFstreamTest, CloseOutOfScope) { 54 FILE* raw_stream = fopen("/dev/null", "r"); 55 ASSERT_NE(nullptr, raw_stream); 56 { 57 ScopedFstream scoped_stream(raw_stream); 58 ASSERT_EQ(raw_stream, scoped_stream.get()); 59 ASSERT_EQ(raw_stream, *scoped_stream); 60 ASSERT_TRUE(scoped_stream); 61 } 62 // We don't have a direct way to see that the file was closed. 63 } 64 65 TEST(ScopedFileTest, Reset) { 66 int raw_fd1 = open("/dev/null", O_RDONLY); 67 int raw_fd2 = open("/dev/zero", O_RDONLY); 68 ASSERT_GE(raw_fd1, 0); 69 ASSERT_GE(raw_fd2, 0); 70 { 71 ScopedFile scoped_file(raw_fd1); 72 ASSERT_EQ(raw_fd1, scoped_file.get()); 73 scoped_file.reset(raw_fd2); 74 ASSERT_EQ(raw_fd2, scoped_file.get()); 75 ASSERT_NE(0, close(raw_fd1)); // Should fail when closing twice. 76 scoped_file.reset(); 77 ASSERT_NE(0, close(raw_fd2)); 78 scoped_file.reset(open("/dev/null", O_RDONLY)); 79 ASSERT_GE(scoped_file.get(), 0); 80 } 81 } 82 83 TEST(ScopedFileTest, Release) { 84 int raw_fd = open("/dev/null", O_RDONLY); 85 ASSERT_GE(raw_fd, 0); 86 { 87 ScopedFile scoped_file(raw_fd); 88 ASSERT_EQ(raw_fd, scoped_file.release()); 89 ASSERT_FALSE(scoped_file); 90 } 91 ASSERT_EQ(0, close(raw_fd)); 92 } 93 94 TEST(ScopedFileTest, MoveCtor) { 95 int raw_fd1 = open("/dev/null", O_RDONLY); 96 int raw_fd2 = open("/dev/zero", O_RDONLY); 97 ASSERT_GE(raw_fd1, 0); 98 ASSERT_GE(raw_fd2, 0); 99 { 100 ScopedFile scoped_file1(ScopedFile{raw_fd1}); 101 ScopedFile scoped_file2(std::move(scoped_file1)); 102 ASSERT_EQ(-1, scoped_file1.get()); 103 ASSERT_EQ(-1, *scoped_file1); 104 ASSERT_FALSE(scoped_file1); 105 ASSERT_EQ(raw_fd1, scoped_file2.get()); 106 107 scoped_file1.reset(raw_fd2); 108 ASSERT_EQ(raw_fd2, scoped_file1.get()); 109 } 110 ASSERT_NE(0, close(raw_fd1)); // Should fail when closing twice. 111 ASSERT_NE(0, close(raw_fd2)); 112 } 113 114 TEST(ScopedFileTest, MoveAssignment) { 115 int raw_fd1 = open("/dev/null", O_RDONLY); 116 int raw_fd2 = open("/dev/zero", O_RDONLY); 117 ASSERT_GE(raw_fd1, 0); 118 ASSERT_GE(raw_fd2, 0); 119 { 120 ScopedFile scoped_file1(raw_fd1); 121 ScopedFile scoped_file2(raw_fd2); 122 scoped_file2 = std::move(scoped_file1); 123 ASSERT_EQ(-1, scoped_file1.get()); 124 ASSERT_FALSE(scoped_file1); 125 ASSERT_EQ(raw_fd1, scoped_file2.get()); 126 ASSERT_NE(0, close(raw_fd2)); 127 128 scoped_file1 = std::move(scoped_file2); 129 ASSERT_EQ(raw_fd1, scoped_file1.get()); 130 ASSERT_EQ(-1, scoped_file2.get()); 131 } 132 ASSERT_NE(0, close(raw_fd1)); 133 } 134 135 // File descriptors are capabilities and hence can be security critical. A 136 // failed close() suggests the memory ownership of the file is wrong and we 137 // might have leaked a capability. 138 TEST(ScopedFileTest, CloseFailureIsFatal) { 139 int raw_fd = open("/dev/null", O_RDONLY); 140 ASSERT_DEATH( 141 { 142 ScopedFile scoped_file(raw_fd); 143 ASSERT_EQ(0, close(raw_fd)); 144 }, 145 ""); 146 } 147 148 } // namespace 149 } // namespace base 150 } // namespace perfetto 151