| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <android-base/cmsg.h> |
| |
| #include <android-base/file.h> |
| #include <android-base/logging.h> |
| #include <android-base/unique_fd.h> |
| #include <gtest/gtest.h> |
| |
| #if !defined(_WIN32) |
| |
| using android::base::ReceiveFileDescriptors; |
| using android::base::SendFileDescriptors; |
| using android::base::unique_fd; |
| |
| static ino_t GetInode(int fd) { |
| struct stat st; |
| if (fstat(fd, &st) != 0) { |
| PLOG(FATAL) << "fstat failed"; |
| } |
| |
| return st.st_ino; |
| } |
| |
| struct CmsgTest : ::testing::TestWithParam<bool> { |
| bool Seqpacket() { return GetParam(); } |
| |
| void SetUp() override { |
| ASSERT_TRUE( |
| android::base::Socketpair(Seqpacket() ? SOCK_SEQPACKET : SOCK_STREAM, &send, &recv)); |
| int dup1 = dup(tmp1.fd); |
| ASSERT_NE(-1, dup1); |
| int dup2 = dup(tmp2.fd); |
| ASSERT_NE(-1, dup2); |
| |
| fd1.reset(dup1); |
| fd2.reset(dup2); |
| |
| ino1 = GetInode(dup1); |
| ino2 = GetInode(dup2); |
| } |
| |
| unique_fd send; |
| unique_fd recv; |
| |
| TemporaryFile tmp1; |
| TemporaryFile tmp2; |
| |
| unique_fd fd1; |
| unique_fd fd2; |
| |
| ino_t ino1; |
| ino_t ino2; |
| }; |
| |
| TEST_P(CmsgTest, smoke) { |
| ASSERT_EQ(1, SendFileDescriptors(send.get(), "x", 1, fd1.get())); |
| |
| char buf[2]; |
| unique_fd received; |
| ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 2, &received)); |
| ASSERT_EQ('x', buf[0]); |
| ASSERT_NE(-1, received.get()); |
| |
| ASSERT_EQ(ino1, GetInode(received.get())); |
| } |
| |
| TEST_P(CmsgTest, msg_trunc) { |
| ASSERT_EQ(2, SendFileDescriptors(send.get(), "ab", 2, fd1.get(), fd2.get())); |
| |
| char buf[2]; |
| unique_fd received1, received2; |
| |
| ssize_t rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2); |
| if (Seqpacket()) { |
| ASSERT_EQ(-1, rc); |
| ASSERT_EQ(EMSGSIZE, errno); |
| ASSERT_EQ(-1, received1.get()); |
| ASSERT_EQ(-1, received2.get()); |
| } else { |
| ASSERT_EQ(1, rc); |
| ASSERT_NE(-1, received1.get()); |
| ASSERT_NE(-1, received2.get()); |
| ASSERT_EQ(ino1, GetInode(received1.get())); |
| ASSERT_EQ(ino2, GetInode(received2.get())); |
| ASSERT_EQ(1, read(recv.get(), buf, 2)); |
| } |
| } |
| |
| TEST_P(CmsgTest, msg_ctrunc) { |
| ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get())); |
| |
| char buf[2]; |
| unique_fd received; |
| ASSERT_EQ(-1, ReceiveFileDescriptors(recv.get(), buf, 1, &received)); |
| ASSERT_EQ(EMSGSIZE, errno); |
| ASSERT_EQ(-1, received.get()); |
| } |
| |
| TEST_P(CmsgTest, peek) { |
| ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get())); |
| |
| char buf[2]; |
| ASSERT_EQ(1, ::recv(recv.get(), buf, sizeof(buf), MSG_PEEK)); |
| ASSERT_EQ('a', buf[0]); |
| |
| unique_fd received; |
| ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received)); |
| ASSERT_EQ(ino1, GetInode(received.get())); |
| } |
| |
| TEST_P(CmsgTest, stream_fd_association) { |
| if (Seqpacket()) { |
| return; |
| } |
| |
| // fds are associated with the first byte of the write. |
| ASSERT_EQ(1, TEMP_FAILURE_RETRY(write(send.get(), "a", 1))); |
| ASSERT_EQ(2, SendFileDescriptors(send.get(), "bc", 2, fd1.get())); |
| ASSERT_EQ(1, SendFileDescriptors(send.get(), "d", 1, fd2.get())); |
| char buf[2]; |
| ASSERT_EQ(2, TEMP_FAILURE_RETRY(read(recv.get(), buf, 2))); |
| ASSERT_EQ(0, memcmp(buf, "ab", 2)); |
| |
| std::vector<unique_fd> received1; |
| ssize_t rc = ReceiveFileDescriptorVector(recv.get(), buf, 1, 1, &received1); |
| ASSERT_EQ(1, rc); |
| ASSERT_EQ('c', buf[0]); |
| ASSERT_TRUE(received1.empty()); |
| |
| unique_fd received2; |
| rc = ReceiveFileDescriptors(recv.get(), buf, 1, &received2); |
| ASSERT_EQ(1, rc); |
| ASSERT_EQ('d', buf[0]); |
| ASSERT_EQ(ino2, GetInode(received2.get())); |
| } |
| |
| TEST_P(CmsgTest, multiple_fd_ordering) { |
| ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get(), fd2.get())); |
| |
| char buf[2]; |
| unique_fd received1, received2; |
| ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1, &received2)); |
| |
| ASSERT_NE(-1, received1.get()); |
| ASSERT_NE(-1, received2.get()); |
| |
| ASSERT_EQ(ino1, GetInode(received1.get())); |
| ASSERT_EQ(ino2, GetInode(received2.get())); |
| } |
| |
| TEST_P(CmsgTest, separate_fd_ordering) { |
| ASSERT_EQ(1, SendFileDescriptors(send.get(), "a", 1, fd1.get())); |
| ASSERT_EQ(1, SendFileDescriptors(send.get(), "b", 1, fd2.get())); |
| |
| char buf[2]; |
| unique_fd received1, received2; |
| ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received1)); |
| ASSERT_EQ(1, ReceiveFileDescriptors(recv.get(), buf, 1, &received2)); |
| |
| ASSERT_NE(-1, received1.get()); |
| ASSERT_NE(-1, received2.get()); |
| |
| ASSERT_EQ(ino1, GetInode(received1.get())); |
| ASSERT_EQ(ino2, GetInode(received2.get())); |
| } |
| |
| TEST_P(CmsgTest, separate_fds_no_coalescing) { |
| unique_fd sent1(dup(tmp1.fd)); |
| unique_fd sent2(dup(tmp2.fd)); |
| |
| ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd1.get())); |
| ASSERT_EQ(1, SendFileDescriptors(send.get(), "", 1, fd2.get())); |
| |
| char buf[2]; |
| std::vector<unique_fd> received; |
| ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received)); |
| ASSERT_EQ(1U, received.size()); |
| ASSERT_EQ(ino1, GetInode(received[0].get())); |
| |
| ASSERT_EQ(1, ReceiveFileDescriptorVector(recv.get(), buf, 2, 2, &received)); |
| ASSERT_EQ(1U, received.size()); |
| ASSERT_EQ(ino2, GetInode(received[0].get())); |
| } |
| |
| INSTANTIATE_TEST_CASE_P(CmsgTest, CmsgTest, testing::Bool()); |
| |
| #endif |