// Copyright 2018 The gVisor Authors.
//
// 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 <errno.h>
#include <fcntl.h>
#include <linux/unistd.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include "gtest/gtest.h"
#include "test/util/file_descriptor.h"
#include "test/util/temp_path.h"
#include "test/util/test_util.h"

namespace gvisor {
namespace testing {

namespace {

class Pread64Test : public ::testing::Test {
  void SetUp() override {
    name_ = NewTempAbsPath();
    ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_CREAT, 0644));
  }

  void TearDown() override { unlink(name_.c_str()); }

 public:
  std::string name_;
};

TEST(Pread64TestNoTempFile, BadFileDescriptor) {
  char buf[1024];
  EXPECT_THAT(pread64(-1, buf, 1024, 0), SyscallFailsWithErrno(EBADF));
}

TEST_F(Pread64Test, ZeroBuffer) {
  const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_RDWR));

  char msg[] = "hello world";
  EXPECT_THAT(pwrite64(fd.get(), msg, strlen(msg), 0),
              SyscallSucceedsWithValue(strlen(msg)));

  char buf[10];
  EXPECT_THAT(pread64(fd.get(), buf, 0, 0), SyscallSucceedsWithValue(0));
}

TEST_F(Pread64Test, BadBuffer) {
  const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_RDWR));

  char msg[] = "hello world";
  EXPECT_THAT(pwrite64(fd.get(), msg, strlen(msg), 0),
              SyscallSucceedsWithValue(strlen(msg)));

  char* bad_buffer = nullptr;
  EXPECT_THAT(pread64(fd.get(), bad_buffer, 1024, 0),
              SyscallFailsWithErrno(EFAULT));
}

TEST_F(Pread64Test, WriteOnlyNotReadable) {
  const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_WRONLY));

  char buf[1024];
  EXPECT_THAT(pread64(fd.get(), buf, 1024, 0), SyscallFailsWithErrno(EBADF));
}

TEST_F(Pread64Test, Pread64WithOpath) {
  const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
  const FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH));

  char buf[1024];
  EXPECT_THAT(pread64(fd.get(), buf, 1024, 0), SyscallFailsWithErrno(EBADF));
}

TEST_F(Pread64Test, DirNotReadable) {
  const FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(GetAbsoluteTestTmpdir(), O_RDONLY));

  char buf[1024];
  EXPECT_THAT(pread64(fd.get(), buf, 1024, 0), SyscallFailsWithErrno(EISDIR));
}

TEST_F(Pread64Test, BadOffset) {
  const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_RDONLY));

  char buf[1024];
  EXPECT_THAT(pread64(fd.get(), buf, 1024, -1), SyscallFailsWithErrno(EINVAL));
}

TEST_F(Pread64Test, OffsetNotIncremented) {
  const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_RDWR));

  char msg[] = "hello world";
  EXPECT_THAT(write(fd.get(), msg, strlen(msg)),
              SyscallSucceedsWithValue(strlen(msg)));
  int offset;
  EXPECT_THAT(offset = lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds());

  char buf1[1024];
  EXPECT_THAT(pread64(fd.get(), buf1, 1024, 0),
              SyscallSucceedsWithValue(strlen(msg)));
  EXPECT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(offset));

  char buf2[1024];
  EXPECT_THAT(pread64(fd.get(), buf2, 1024, 3),
              SyscallSucceedsWithValue(strlen(msg) - 3));
  EXPECT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(offset));
}

TEST_F(Pread64Test, EndOfFile) {
  const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_RDONLY));

  char buf[1024];
  EXPECT_THAT(pread64(fd.get(), buf, 1024, 0), SyscallSucceedsWithValue(0));
}

int memfd_create(const std::string& name, unsigned int flags) {
  return syscall(__NR_memfd_create, name.c_str(), flags);
}

TEST_F(Pread64Test, Overflow) {
  int f = memfd_create("negative", 0);
  const FileDescriptor fd(f);

  EXPECT_THAT(ftruncate(fd.get(), 0x7fffffffffffffffull), SyscallSucceeds());

  char buf[10];
  EXPECT_THAT(pread64(fd.get(), buf, sizeof(buf), 0x7fffffffffffffffull),
              SyscallFailsWithErrno(EINVAL));
}

TEST(Pread64TestNoTempFile, CantReadSocketPair) {
  int sock_fds[2];
  EXPECT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fds), SyscallSucceeds());

  char buf[1024];
  EXPECT_THAT(pread64(sock_fds[0], buf, 1024, 0),
              SyscallFailsWithErrno(ESPIPE));
  EXPECT_THAT(pread64(sock_fds[1], buf, 1024, 0),
              SyscallFailsWithErrno(ESPIPE));

  EXPECT_THAT(close(sock_fds[0]), SyscallSucceeds());
  EXPECT_THAT(close(sock_fds[1]), SyscallSucceeds());
}

TEST(Pread64TestNoTempFile, CantReadPipe) {
  char buf[1024];

  int pipe_fds[2];
  EXPECT_THAT(pipe(pipe_fds), SyscallSucceeds());

  EXPECT_THAT(pread64(pipe_fds[0], buf, 1024, 0),
              SyscallFailsWithErrno(ESPIPE));

  EXPECT_THAT(close(pipe_fds[0]), SyscallSucceeds());
  EXPECT_THAT(close(pipe_fds[1]), SyscallSucceeds());
}

}  // namespace

}  // namespace testing
}  // namespace gvisor
