diff --git a/starboard/android/shared/test_filters.py b/starboard/android/shared/test_filters.py index 7c90f5484614..3d30f3deac2e 100644 --- a/starboard/android/shared/test_filters.py +++ b/starboard/android/shared/test_filters.py @@ -65,6 +65,12 @@ 'PosixDirectoryOpenTest.SunnyDayStaticContent', 'PosixFileGetPathInfoTest.WorksOnStaticContentDirectories', + # These POSIX tests should be disabled until asset manager starboard + # extension is implemented. + 'PosixFileGetInfoTest.WorksOnStaticContentFiles', + 'PosixFileReadTest/*.ReadStaticContent', + 'PosixFileSeekTest.FromEndInStaticContentWorks', + # These tests are disabled due to not receiving the kEndOfStream # player state update within the specified timeout. 'SbPlayerGetAudioConfigurationTests/SbPlayerGetAudioConfigurationTest.NoInput/*', diff --git a/starboard/common/file.cc b/starboard/common/file.cc index 3dc007e761e2..4ce81e6c2871 100644 --- a/starboard/common/file.cc +++ b/starboard/common/file.cc @@ -15,6 +15,8 @@ #include "starboard/common/file.h" #include +#include +#include #include #include @@ -40,6 +42,26 @@ bool DirectoryCloseLogFailure(const char* path, SbDirectory dir) { } // namespace +ssize_t ReadAll(int fd, void* data, int size) { + if (fd < 0 || size < 0) { + return -1; + } + ssize_t bytes_read = 0; + ssize_t rv; + do { + // Needs to cast void* to char* as MSVC returns error for pointer + // arithmetic. + rv = + read(fd, reinterpret_cast(data) + bytes_read, size - bytes_read); + if (rv <= 0) { + break; + } + bytes_read += rv; + } while (bytes_read < size); + + return bytes_read ? bytes_read : rv; +} + void RecordFileWriteStat(int write_file_result) { auto& stats_tracker = StatsTrackerContainer::GetInstance()->stats_tracker(); if (write_file_result <= 0) { diff --git a/starboard/common/file.h b/starboard/common/file.h index 3965042eb768..d9c1bbc0ef73 100644 --- a/starboard/common/file.h +++ b/starboard/common/file.h @@ -22,8 +22,12 @@ #include "starboard/file.h" +#include + namespace starboard { +ssize_t ReadAll(int fd, void* data, int size); + void RecordFileWriteStat(int write_file_result); // Deletes the file, symlink or directory at |path|. When |path| is a directory, diff --git a/starboard/elf_loader/exported_symbols.cc b/starboard/elf_loader/exported_symbols.cc index e065f069a2bb..abcce34c92b2 100644 --- a/starboard/elf_loader/exported_symbols.cc +++ b/starboard/elf_loader/exported_symbols.cc @@ -51,6 +51,7 @@ #include "starboard/mutex.h" #include "starboard/player.h" #if SB_API_VERSION >= 16 +#include "starboard/shared/modular/starboard_layer_posix_file_abi_wrappers.h" #include "starboard/shared/modular/starboard_layer_posix_mmap_abi_wrappers.h" #include "starboard/shared/modular/starboard_layer_posix_pthread_abi_wrappers.h" #include "starboard/shared/modular/starboard_layer_posix_stat_abi_wrappers.h" @@ -461,10 +462,12 @@ ExportedSymbols::ExportedSymbols() { REGISTER_SYMBOL(free); REGISTER_SYMBOL(freeaddrinfo); REGISTER_SYMBOL(freeifaddrs); + REGISTER_SYMBOL(fstat); REGISTER_SYMBOL(getaddrinfo); REGISTER_SYMBOL(getifaddrs); REGISTER_SYMBOL(getsockname); REGISTER_SYMBOL(listen); + REGISTER_SYMBOL(lseek); REGISTER_SYMBOL(malloc); REGISTER_SYMBOL(mkdir); REGISTER_SYMBOL(mprotect); @@ -472,6 +475,7 @@ ExportedSymbols::ExportedSymbols() { REGISTER_SYMBOL(munmap); REGISTER_SYMBOL(open); REGISTER_SYMBOL(posix_memalign); + REGISTER_SYMBOL(read); REGISTER_SYMBOL(realloc); REGISTER_SYMBOL(recv); REGISTER_SYMBOL(send); @@ -495,9 +499,11 @@ ExportedSymbols::ExportedSymbols() { // TODO: b/316603042 - Detect via NPLB and only add the wrapper if needed. map_["clock_gettime"] = reinterpret_cast(&__abi_wrap_clock_gettime); + map_["fstat"] = reinterpret_cast(&__abi_wrap_fstat); map_["gettimeofday"] = reinterpret_cast(&__abi_wrap_gettimeofday); map_["gmtime_r"] = reinterpret_cast(&__abi_wrap_gmtime_r); + map_["lseek"] = reinterpret_cast(&__abi_wrap_lseek); map_["mmap"] = reinterpret_cast(&__abi_wrap_mmap); map_["pthread_cond_broadcast"] = reinterpret_cast(&__abi_wrap_pthread_cond_broadcast); @@ -553,6 +559,7 @@ ExportedSymbols::ExportedSymbols() { reinterpret_cast(&__abi_wrap_pthread_setspecific); map_["pthread_setname_np"] = reinterpret_cast(&__abi_wrap_pthread_setname_np); + map_["read"] = reinterpret_cast(&__abi_wrap_read); map_["stat"] = reinterpret_cast(&__abi_wrap_stat); map_["time"] = reinterpret_cast(&__abi_wrap_time); diff --git a/starboard/nplb/BUILD.gn b/starboard/nplb/BUILD.gn index 346ee856415b..e303c8bde456 100644 --- a/starboard/nplb/BUILD.gn +++ b/starboard/nplb/BUILD.gn @@ -144,8 +144,11 @@ target(gtest_target_type, "nplb") { "posix_compliance/posix_directory_can_open_test.cc", "posix_compliance/posix_directory_create_test.cc", "posix_compliance/posix_file_close_test.cc", + "posix_compliance/posix_file_get_info_test.cc", "posix_compliance/posix_file_get_path_info_test.cc", "posix_compliance/posix_file_open_test.cc", + "posix_compliance/posix_file_read_test.cc", + "posix_compliance/posix_file_seek_test.cc", "posix_compliance/posix_memory_allocate_aligned_test.cc", "posix_compliance/posix_memory_allocate_test.cc", "posix_compliance/posix_memory_deallocate_aligned_test.cc", diff --git a/starboard/nplb/posix_compliance/posix_file_close_test.cc b/starboard/nplb/posix_compliance/posix_file_close_test.cc index 7b79b2b00169..ff124f2d41ed 100644 --- a/starboard/nplb/posix_compliance/posix_file_close_test.cc +++ b/starboard/nplb/posix_compliance/posix_file_close_test.cc @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#if SB_API_VERSION >= 16 - // close is partially tested in posix_file_open_test.cc. #include @@ -32,5 +30,3 @@ TEST(PosixFileCloseTest, CloseInvalidFails) { } // namespace } // namespace nplb } // namespace starboard - -#endif // SB_API_VERSION >= 16 diff --git a/starboard/nplb/posix_compliance/posix_file_get_info_test.cc b/starboard/nplb/posix_compliance/posix_file_get_info_test.cc new file mode 100644 index 000000000000..192fe817cef9 --- /dev/null +++ b/starboard/nplb/posix_compliance/posix_file_get_info_test.cc @@ -0,0 +1,97 @@ +// Copyright 2024 The Cobalt Authors. All Rights Reserved. +// +// 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 +#include +#include +#include + +#include +#include + +#include "starboard/common/time.h" +#include "starboard/file.h" +#include "starboard/nplb/file_helpers.h" +#include "starboard/system.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace starboard { +namespace nplb { +namespace { + +inline int64_t TimeTToWindowsUsec(time_t time) { + int64_t posix_usec = static_cast(time) * 1000000; + return PosixTimeToWindowsTime(posix_usec); +} + +TEST(PosixFileGetInfoTest, InvalidFileErrors) { + struct stat info; + int result = fstat(-1, &info); + EXPECT_FALSE(result == 0); +} + +TEST(PosixFileGetInfoTest, WorksOnARegularFile) { + // This test is potentially flaky because it's comparing times. So, building + // in extra sensitivity to make flakiness more apparent. + const int kTrials = 100; + for (int i = 0; i < kTrials; ++i) { + // We can't assume filesystem timestamp precision, so go back a minute + // for a better chance to contain the imprecision and rounding errors. + const int64_t kOneMinuteInMicroseconds = 60'000'000; + int64_t time = + PosixTimeToWindowsTime(CurrentPosixTime()) - kOneMinuteInMicroseconds; + + const int kFileSize = 12; + starboard::nplb::ScopedRandomFile random_file(kFileSize); + const std::string& filename = random_file.filename(); + + int file = open(filename.c_str(), O_RDONLY); + ASSERT_TRUE(file >= 0); + + { + struct stat info; + int result = fstat(file, &info); + EXPECT_EQ(kFileSize, info.st_size); + EXPECT_FALSE(S_ISDIR(info.st_mode)); + EXPECT_FALSE(S_ISLNK(info.st_mode)); + EXPECT_LE(time, TimeTToWindowsUsec(info.st_atime)); + EXPECT_LE(time, TimeTToWindowsUsec(info.st_atime)); + EXPECT_LE(time, TimeTToWindowsUsec(info.st_ctime)); + } + + int result = close(file); + EXPECT_TRUE(result == 0); + } +} + +TEST(PosixFileGetInfoTest, WorksOnStaticContentFiles) { + int count = 1; + for (auto filename : GetFileTestsFilePaths()) { + int file = open(filename.c_str(), O_RDONLY); + ASSERT_TRUE(file >= 0); + + struct stat info; + EXPECT_TRUE(fstat(file, &info) == 0); + size_t content_length = GetTestFileExpectedContent(filename).length(); + EXPECT_EQ(content_length, info.st_size); + EXPECT_FALSE(S_ISDIR(info.st_mode)); + EXPECT_FALSE(S_ISLNK(info.st_mode)); + + EXPECT_TRUE(close(file) == 0); + } +} + +} // namespace +} // namespace nplb +} // namespace starboard diff --git a/starboard/nplb/posix_compliance/posix_file_get_path_info_test.cc b/starboard/nplb/posix_compliance/posix_file_get_path_info_test.cc index 2a5a857cc5b6..fbada38a57dc 100644 --- a/starboard/nplb/posix_compliance/posix_file_get_path_info_test.cc +++ b/starboard/nplb/posix_compliance/posix_file_get_path_info_test.cc @@ -51,13 +51,6 @@ TEST(PosixFileGetPathInfoTest, WorksOnARegularFile) { // for a better chance to contain the imprecision and rounding errors. const int64_t kOneSecondInMicroseconds = 1'000'000; int64_t time = PosixTimeToWindowsTime(CurrentPosixTime()); -#if !SB_HAS_QUIRK(FILESYSTEM_ZERO_FILEINFO_TIME) -#if SB_HAS_QUIRK(FILESYSTEM_COARSE_ACCESS_TIME) - // On platforms with coarse access time, we assume 1 day precision and go - // back 2 days to avoid rounding issues. - const int64_t kOneDayInMicroseconds = 1'000'000LL * 60LL * 60LL * 24LL; -#endif // FILESYSTEM_COARSE_ACCESS_TIME -#endif // FILESYSTEM_ZERO_FILEINFO_TIME const int kFileSize = 12; ScopedRandomFile random_file(kFileSize); @@ -69,23 +62,12 @@ TEST(PosixFileGetPathInfoTest, WorksOnARegularFile) { EXPECT_EQ(kFileSize, file_info.st_size); EXPECT_FALSE(S_ISDIR(file_info.st_mode)); EXPECT_FALSE(S_ISLNK(file_info.st_mode)); -#if SB_HAS_QUIRK(FILESYSTEM_ZERO_FILEINFO_TIME) - EXPECT_LE(0, TimeTToWindowsUsecTest(file_info.at_ctime)); - EXPECT_LE(0, TimeTToWindowsUsecTest(file_info.mt_ctime)); - EXPECT_LE(0, TimeTToWindowsUsecTest(file_info.st_ctime)); -#else EXPECT_NEAR(time, TimeTToWindowsUsecTest(file_info.st_mtime), kOneSecondInMicroseconds); -#if SB_HAS_QUIRK(FILESYSTEM_COARSE_ACCESS_TIME) - EXPECT_NEAR(time, TimeTToWindowsUsecTest(file_info.at_ctime), - 2 * kOneDayInMicroseconds); -#else EXPECT_NEAR(time, TimeTToWindowsUsecTest(file_info.st_atime), kOneSecondInMicroseconds); -#endif // FILESYSTEM_COARSE_ACCESS_TIME EXPECT_NEAR(time, TimeTToWindowsUsecTest(file_info.st_ctime), kOneSecondInMicroseconds); -#endif // FILESYSTEM_ZERO_FILEINFO_TIME } } } diff --git a/starboard/nplb/posix_compliance/posix_file_helpers.cc b/starboard/nplb/posix_compliance/posix_file_helpers.cc deleted file mode 100644 index 46d0e39862c7..000000000000 --- a/starboard/nplb/posix_compliance/posix_file_helpers.cc +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2024 The Cobalt Authors. All Rights Reserved. -// -// 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 "starboard/nplb/posix_compliance/posix_file_helpers.h" - -#include -#include - -#include -#include -#include - -#include "starboard/configuration_constants.h" -#include "starboard/directory.h" -#include "starboard/file.h" -#include "starboard/shared/posix/file_internal.h" -#include "starboard/system.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace starboard { -namespace nplb { - -// static -std::string ScopedRandomFile::MakeRandomFilePath() { - std::ostringstream filename_stream; - filename_stream << GetTempDir(); - if (!filename_stream.tellp()) { - return ""; - } - - filename_stream << kSbFileSepChar << MakeRandomFilename(); - return filename_stream.str(); -} - -std::string ScopedRandomFile::MakeRandomFile(int length) { - std::string filename = MakeRandomFilePath(); - if (filename.empty()) { - return filename; - } - - int file = open(filename.c_str(), O_CREAT | O_WRONLY); - EXPECT_TRUE(fcntl(file, F_GETFD)); - if (!fcntl(file, F_GETFD)) { - return ""; - } - - char* data = new char[length]; - for (int i = 0; i < length; ++i) { - data[i] = static_cast(i & 0xFF); - } - - bool result = close(file); - EXPECT_TRUE(result) << "Failed to close " << filename; - delete[] data; - return filename; -} - -} // namespace nplb -} // namespace starboard diff --git a/starboard/nplb/posix_compliance/posix_file_helpers.h b/starboard/nplb/posix_compliance/posix_file_helpers.h deleted file mode 100644 index 758594f3bc6d..000000000000 --- a/starboard/nplb/posix_compliance/posix_file_helpers.h +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2024 The Cobalt Authors. All Rights Reserved. -// -// 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. - -#ifndef STARBOARD_NPLB_POSIX_COMPLIANCE_POSIX_FILE_HELPERS_H_ -#define STARBOARD_NPLB_POSIX_COMPLIANCE_POSIX_FILE_HELPERS_H_ - -#include -#include - -#include "starboard/file.h" - -namespace starboard { -namespace nplb { - -// Gets the temporary directory in which ScopedRandomFile places its files. -std::string GetTempDir(); - -// Creates a random file of the given length, and deletes it when the instance -// falls out of scope. -class ScopedRandomFile { - public: - enum { - kDefaultLength = 64, - }; - - enum Create { - kCreate, - kDontCreate, - }; - - // Will create a file of |kDefaultLength| bytes long. - ScopedRandomFile() : size_(kDefaultLength) { - filename_ = MakeRandomFile(size_); - } - - // Will create a file |length| bytes long. - explicit ScopedRandomFile(int length) : size_(length) { - filename_ = MakeRandomFile(size_); - } - - // Will either create a file |length| bytes long, or will just generate a - // filename. |create| is whether to create the file or not. - ScopedRandomFile(int length, Create create) : size_(length) { - filename_ = - (create == kCreate ? MakeRandomFile(size_) : MakeRandomFilePath()); - } - - // Will either create a file of |kDefaultLength| bytes long, or will just - // generate a filename. |create| is whether to create the file or not. - explicit ScopedRandomFile(Create create) : size_(kDefaultLength) { - filename_ = - (create == kCreate ? MakeRandomFile(size_) : MakeRandomFilePath()); - } - - ~ScopedRandomFile() { SbFileDelete(filename_.c_str()); } - - // Creates and returns a random filename (no path), but does not create the - // file. - static std::string MakeRandomFilename(); - - // Returns the filename generated for this file. - const std::string& filename() const { return filename_; } - - // Returns the SPECIFIED size of the file (not the size returned by the - // filesystem). - const int size() const { return size_; } - - // Checks |buffer| of size |size| against this class's write pattern, offset - // by |pattern_offset|. Failures print the original line number |line|. - static void ExpectPattern(int pattern_offset, - void* buffer, - int size, - int line); - - private: - // Creates a file with a random name and |length| bytes, returning the path to - // the new file. - static std::string MakeRandomFile(int length); - - // Creates and returns a path to a random file, but does not create the file. - static std::string MakeRandomFilePath(); - - std::string filename_; - int size_; -}; - -} // namespace nplb -} // namespace starboard - -#endif // STARBOARD_NPLB_POSIX_COMPLIANCE_POSIX_FILE_HELPERS_H_ diff --git a/starboard/nplb/posix_compliance/posix_file_open_test.cc b/starboard/nplb/posix_compliance/posix_file_open_test.cc index 6656145e0537..5a4c546c9f08 100644 --- a/starboard/nplb/posix_compliance/posix_file_open_test.cc +++ b/starboard/nplb/posix_compliance/posix_file_open_test.cc @@ -12,14 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -#if SB_API_VERSION >= 16 - #include #include #include #include "starboard/file.h" -#include "starboard/nplb/posix_compliance/posix_file_helpers.h" +#include "starboard/nplb/file_helpers.h" #include "testing/gtest/include/gtest/gtest.h" namespace starboard { @@ -133,5 +131,3 @@ TEST(PosixFileOpenTest, OpenTruncatedDoesNotCreateNonExistingFile) { } // namespace } // namespace nplb } // namespace starboard - -#endif // SB_API_VERSION >= 16 diff --git a/starboard/nplb/posix_compliance/posix_file_read_test.cc b/starboard/nplb/posix_compliance/posix_file_read_test.cc new file mode 100644 index 000000000000..b6ce49e7957d --- /dev/null +++ b/starboard/nplb/posix_compliance/posix_file_read_test.cc @@ -0,0 +1,269 @@ +// Copyright 2024 The Cobalt Authors. All Rights Reserved. +// +// 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 +#include +#include +#include +#include + +#include "starboard/common/file.h" +#include "starboard/nplb/file_helpers.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace starboard { +namespace nplb { +namespace { + +// Sets up an empty test fixture, required for typed tests. +template +class PosixFileReadTest : public testing::Test {}; + +class PosixRead { + public: + static ssize_t Read(int file, void* buf, size_t nbyte) { + return read(file, buf, nbyte); + } +}; + +class PosixReadAll { + public: + static ssize_t Read(int file, void* data, int size) { + return ReadAll(file, data, size); + } +}; + +typedef testing::Types PosixFileReadTestTypes; + +template +size_t array_size(const T (&)[n]) { + return n; +} + +TYPED_TEST_CASE(PosixFileReadTest, PosixFileReadTestTypes); + +const int kBufferLength = 16 * 1024; + +TYPED_TEST(PosixFileReadTest, InvalidFileErrors) { + char buffer[kBufferLength]; + int result = TypeParam::Read(-1, buffer, kBufferLength); + EXPECT_EQ(-1, result); +} + +TYPED_TEST(PosixFileReadTest, BasicReading) { + // Create a pattern file that is not an even multiple of the buffer size, + // but is over several times the size of the buffer. + const int kFileSize = kBufferLength * 16 / 3; + ScopedRandomFile random_file(kFileSize); + const std::string& filename = random_file.filename(); + + int file = open(filename.c_str(), O_RDONLY); + ASSERT_TRUE(file >= 0); + + // Create a bigger buffer than necessary, so we can test the memory around + // the portion given to SbFileRead. + const int kRealBufferLength = kBufferLength * 2; + char real_buffer[kRealBufferLength] = {0}; + const int kBufferOffset = kBufferLength / 2; + char* buffer = real_buffer + kBufferOffset; + + // Initialize to some arbitrary pattern so we can verify it later. + for (int i = 0; i < kRealBufferLength; ++i) { + real_buffer[i] = '\xCD'; + } + + // Read and check the whole file. + int total = 0; + int previous_total = 0; + int max = 0; + while (true) { + int bytes_read = TypeParam::Read(file, buffer, kBufferLength); + if (bytes_read == 0) { + break; + } + + // Check that we didn't read more than the buffer size. + EXPECT_GE(kBufferLength, bytes_read); + + // Check that we didn't get an error. + EXPECT_LT(0, bytes_read); + + // Do some accounting to check later. + previous_total = total; + total += bytes_read; + if (bytes_read > max) { + max = bytes_read; + } + + ScopedRandomFile::ExpectPattern(previous_total, buffer, bytes_read, + __LINE__); + } + + // Check that we read the whole file. + EXPECT_EQ(kFileSize, total); + + // check that we didn't write over any other parts of the buffer. + for (int i = 0; i < kBufferOffset; ++i) { + EXPECT_EQ('\xCD', real_buffer[i]); + } + + for (int i = kBufferOffset + max; i < kRealBufferLength; ++i) { + EXPECT_EQ('\xCD', real_buffer[i]); + } + + bool result = close(file); + EXPECT_TRUE(result == 0); +} + +TYPED_TEST(PosixFileReadTest, ReadZeroBytes) { + const int kFileSize = kBufferLength; + ScopedRandomFile random_file(kFileSize); + const std::string& filename = random_file.filename(); + + int file = open(filename.c_str(), O_RDONLY); + ASSERT_TRUE(file >= 0); + + // Create a bigger buffer than necessary, so we can test the memory around + // the portion given to SbFileRead. + const int kRealBufferLength = kBufferLength * 2; + char real_buffer[kRealBufferLength] = {0}; + const int kBufferOffset = kBufferLength / 2; + char* buffer = real_buffer + kBufferOffset; + + // Initialize to some arbitrary pattern so we can verify it later. + for (int i = 0; i < kRealBufferLength; ++i) { + real_buffer[i] = '\xCD'; + } + + // Read zero bytes. + for (int i = 0; i < 10; ++i) { + int bytes_read = TypeParam::Read(file, buffer, 0); + EXPECT_EQ(0, bytes_read); + } + + for (int i = 0; i < kRealBufferLength; ++i) { + EXPECT_EQ('\xCD', real_buffer[i]); + } + + int result = close(file); + EXPECT_TRUE(result == 0); +} + +TYPED_TEST(PosixFileReadTest, ReadFromMiddle) { + const int kFileSize = kBufferLength * 2; + ScopedRandomFile random_file(kFileSize); + const std::string& filename = random_file.filename(); + + int file = open(filename.c_str(), O_RDONLY); + ASSERT_TRUE(file >= 0); + + // Create a bigger buffer than necessary, so we can test the memory around + // the portion given to SbFileRead. + const int kRealBufferLength = kBufferLength * 2; + char real_buffer[kRealBufferLength] = {0}; + const int kBufferOffset = kBufferLength / 2; + char* buffer = real_buffer + kBufferOffset; + + // Initialize to some arbitrary pattern so we can verify it later. + for (int i = 0; i < kRealBufferLength; ++i) { + real_buffer[i] = '\xCD'; + } + + // Read from the middle of the file. + int position = static_cast(lseek(file, kFileSize / 4, SEEK_SET)); + EXPECT_EQ(kFileSize / 4, position); + int bytes_read = TypeParam::Read(file, buffer, kBufferLength); + EXPECT_GE(kBufferLength, bytes_read); + EXPECT_LT(0, bytes_read); + + ScopedRandomFile::ExpectPattern(position, buffer, bytes_read, __LINE__); + + for (int i = 0; i < kBufferOffset; ++i) { + EXPECT_EQ('\xCD', real_buffer[i]); + if ('\xCD' != real_buffer[i]) + break; + } + + for (int i = kBufferOffset + bytes_read; i < kRealBufferLength; ++i) { + EXPECT_EQ('\xCD', real_buffer[i]); + if ('\xCD' != real_buffer[i]) + break; + } + + int result = close(file); + EXPECT_TRUE(result == 0); +} + +TYPED_TEST(PosixFileReadTest, ReadStaticContent) { + for (auto filename : GetFileTestsFilePaths()) { + int file = open(filename.c_str(), O_RDONLY); + ASSERT_TRUE(file >= 0) << "Can't open: " << filename; + + // Create a bigger buffer than necessary, so we can test the memory around + // the portion given to SbFileRead. + const int kRealBufferLength = kBufferLength * 2; + char real_buffer[kRealBufferLength] = {0}; + const int kBufferOffset = kBufferLength / 2; + char* buffer = real_buffer + kBufferOffset; + + // Initialize to some arbitrary pattern so we can verify it later. + for (int i = 0; i < kRealBufferLength; ++i) { + real_buffer[i] = '\xCD'; + } + + // Read and check the whole file. + std::string content; + int total = 0; + int max = 0; + while (true) { + int bytes_read = TypeParam::Read(file, buffer, kBufferLength); + if (bytes_read == 0) { + break; + } + + // Check that we didn't read more than the buffer size. + EXPECT_GE(kBufferLength, bytes_read); + + // Check that we didn't get an error. + EXPECT_LT(0, bytes_read); + + // Do some accounting to check later. + total += bytes_read; + if (bytes_read > max) { + max = bytes_read; + } + + // Accumulate the content of the whole file. + content.append(buffer, bytes_read); + } + + // Check that we didn't write over any other parts of the buffer. + for (int i = 0; i < kBufferOffset; ++i) { + EXPECT_EQ('\xCD', real_buffer[i]); + } + + for (int i = kBufferOffset + max; i < kRealBufferLength; ++i) { + EXPECT_EQ('\xCD', real_buffer[i]); + } + + EXPECT_EQ(GetTestFileExpectedContent(filename), content); + + int result = close(file); + EXPECT_TRUE(result == 0); + } +} + +} // namespace +} // namespace nplb +} // namespace starboard diff --git a/starboard/nplb/posix_compliance/posix_file_seek_test.cc b/starboard/nplb/posix_compliance/posix_file_seek_test.cc new file mode 100644 index 000000000000..6473c00f3fde --- /dev/null +++ b/starboard/nplb/posix_compliance/posix_file_seek_test.cc @@ -0,0 +1,170 @@ +// Copyright 2024 The Cobalt Authors. All Rights Reserved. +// +// 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 +#include +#include +#include +#include + +#include +#include + +#include "starboard/file.h" +#include "starboard/nplb/file_helpers.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace starboard { +namespace nplb { +namespace { + +TEST(PosixFileSeekTest, InvalidFileErrors) { + int invalid_fd = -1; + int result = static_cast(lseek(invalid_fd, 50, SEEK_SET)); + EXPECT_EQ(-1, result); + + result = static_cast(lseek(invalid_fd, -50, SEEK_END)); + EXPECT_EQ(-1, result); + + result = static_cast(lseek(invalid_fd, -50, SEEK_CUR)); + EXPECT_EQ(-1, result); + + result = static_cast(lseek(invalid_fd, 50, SEEK_CUR)); + EXPECT_EQ(-1, result); +} + +TEST(PosixFileSeekTest, FromEndWorks) { + starboard::nplb::ScopedRandomFile random_file; + const std::string& filename = random_file.filename(); + int file = open(filename.c_str(), O_RDONLY); + ASSERT_TRUE(file >= 0); + + struct stat info; + int result = fstat(file, &info); + EXPECT_TRUE(result == 0); + + int64_t position = lseek(file, 0, SEEK_END); + EXPECT_EQ(info.st_size, position); + + int64_t target = -(random_file.size() / 6); + position = lseek(file, target, SEEK_END); + EXPECT_EQ(info.st_size + target, position); + + position = lseek(file, -info.st_size, SEEK_END); + EXPECT_EQ(0, position); + + result = close(file); + EXPECT_TRUE(result == 0); +} + +TEST(PosixFileSeekTest, FromCurrentWorks) { + starboard::nplb::ScopedRandomFile random_file; + const std::string& filename = random_file.filename(); + int file = open(filename.c_str(), O_RDONLY); + ASSERT_TRUE(file >= 0); + + struct stat info; + int result = fstat(file, &info); + EXPECT_TRUE(result == 0); + + int64_t position = lseek(file, 0, SEEK_CUR); + EXPECT_EQ(0, position); + + int64_t target = random_file.size() / 6; + position = lseek(file, target, SEEK_CUR); + EXPECT_EQ(target, position); + + position = lseek(file, target, SEEK_CUR); + EXPECT_EQ(target * 2, position); + + position = lseek(file, 0, SEEK_CUR); + EXPECT_EQ(target * 2, position); + + position = lseek(file, info.st_size - position, SEEK_CUR); + EXPECT_EQ(info.st_size, position); + + position = lseek(file, -info.st_size, SEEK_CUR); + EXPECT_EQ(0, position); + + result = close(file); + EXPECT_TRUE(result == 0); +} + +TEST(PosixFileSeekTest, FromBeginWorks) { + starboard::nplb::ScopedRandomFile random_file; + const std::string& filename = random_file.filename(); + int file = open(filename.c_str(), O_RDONLY); + ASSERT_TRUE(file >= 0); + + struct stat info; + int result = fstat(file, &info); + EXPECT_TRUE(result == 0); + + int64_t position = lseek(file, 0, SEEK_SET); + EXPECT_EQ(0, position); + + int64_t target = random_file.size() / 6; + position = lseek(file, target, SEEK_SET); + EXPECT_EQ(target, position); + + target = random_file.size() / 3; + position = lseek(file, target, SEEK_SET); + EXPECT_EQ(target, position); + + target = info.st_size - random_file.size() / 6; + position = lseek(file, target, SEEK_SET); + EXPECT_EQ(target, position); + + position = lseek(file, info.st_size, SEEK_SET); + EXPECT_EQ(info.st_size, position); + + result = close(file); + EXPECT_TRUE(result == 0); +} + +std::string GetTestStaticContentFile() { + std::string filename = GetFileTestsFilePaths().front(); + int content_length = GetTestFileExpectedContent(filename).length(); + EXPECT_GT(content_length, 40); + return filename; +} + +TEST(PosixFileSeekTest, FromEndInStaticContentWorks) { + std::string filename = GetTestStaticContentFile(); + int file = open(filename.c_str(), O_RDONLY); + ASSERT_TRUE(file >= 0); + + int content_length = GetTestFileExpectedContent(filename).length(); + + struct stat info; + int result = fstat(file, &info); + EXPECT_TRUE(result == 0); + + int64_t position = lseek(file, 0, SEEK_END); + EXPECT_EQ(info.st_size, position); + + int64_t target = -(content_length / 6); + position = lseek(file, target, SEEK_END); + EXPECT_EQ(info.st_size + target, position); + + position = lseek(file, -info.st_size, SEEK_END); + EXPECT_EQ(0, position); + + result = close(file); + EXPECT_TRUE(result == 0); +} + +} // namespace +} // namespace nplb +} // namespace starboard diff --git a/starboard/shared/modular/BUILD.gn b/starboard/shared/modular/BUILD.gn index 6003a286e6d0..a8562a40fab3 100644 --- a/starboard/shared/modular/BUILD.gn +++ b/starboard/shared/modular/BUILD.gn @@ -17,6 +17,8 @@ if (sb_is_modular || sb_is_evergreen_compatible) { source_set("starboard_layer_posix_abi_wrappers") { sources = [ + "starboard_layer_posix_file_abi_wrappers.cc", + "starboard_layer_posix_file_abi_wrappers.h", "starboard_layer_posix_mmap_abi_wrappers.cc", "starboard_layer_posix_mmap_abi_wrappers.h", "starboard_layer_posix_pthread_abi_wrappers.cc", @@ -37,6 +39,7 @@ if (sb_is_modular && !sb_is_evergreen && current_toolchain == cobalt_toolchain) { source_set("cobalt_layer_posix_abi_wrappers") { sources = [ + "cobalt_layer_posix_file_abi_wrappers.cc", "cobalt_layer_posix_mmap_abi_wrappers.cc", "cobalt_layer_posix_pthread_abi_wrappers.cc", "cobalt_layer_posix_stat_abi_wrappers.cc", diff --git a/starboard/shared/modular/cobalt_layer_posix_file_abi_wrappers.cc b/starboard/shared/modular/cobalt_layer_posix_file_abi_wrappers.cc new file mode 100644 index 000000000000..4901f49c4613 --- /dev/null +++ b/starboard/shared/modular/cobalt_layer_posix_file_abi_wrappers.cc @@ -0,0 +1,34 @@ +// Copyright 2024 The Cobalt Authors. All Rights Reserved. +// +// 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. + +#if SB_API_VERSION >= 16 + +#include + +extern "C" { + +off_t __abi_wrap_lseek(int fildes, off_t offset, int whence); + +off_t lseek(int fildes, off_t offset, int whence) { + return __abi_wrap_lseek(fildes, offset, whence); +} + +ssize_t __abi_wrap_read(int fildes, void* buf, size_t nbyte); + +ssize_t read(int fildes, void* buf, size_t nbyte) { + return __abi_wrap_read(fildes, buf, nbyte); +} +} + +#endif // SB_API_VERSION >= 16 diff --git a/starboard/shared/modular/cobalt_layer_posix_stat_abi_wrappers.cc b/starboard/shared/modular/cobalt_layer_posix_stat_abi_wrappers.cc index c37d48557a97..64be3a1c4b0e 100644 --- a/starboard/shared/modular/cobalt_layer_posix_stat_abi_wrappers.cc +++ b/starboard/shared/modular/cobalt_layer_posix_stat_abi_wrappers.cc @@ -18,8 +18,13 @@ extern "C" { +int __abi_wrap_fstat(int fildes, struct stat* info); int __abi_wrap_stat(const char* path, struct stat* info); +int fstat(int fildes, struct stat* info) { + return __abi_wrap_fstat(fildes, info); +} + int stat(const char* path, struct stat* info) { return __abi_wrap_stat(path, info); } diff --git a/starboard/shared/modular/starboard_layer_posix_file_abi_wrappers.cc b/starboard/shared/modular/starboard_layer_posix_file_abi_wrappers.cc new file mode 100644 index 000000000000..a02e6a9068bb --- /dev/null +++ b/starboard/shared/modular/starboard_layer_posix_file_abi_wrappers.cc @@ -0,0 +1,30 @@ +// Copyright 2024 The Cobalt Authors. All Rights Reserved. +// +// 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. + +#if SB_API_VERSION >= 16 + +#include "starboard/shared/modular/starboard_layer_posix_file_abi_wrappers.h" + +#include +#include + +musl_off_t __abi_wrap_lseek(int fildes, musl_off_t offset, int whence) { + return static_cast(lseek(fildes, static_cast(offset), whence)); +} + +ssize_t __abi_wrap_read(int fildes, void* buf, size_t nbyte) { + return read(fildes, buf, nbyte); +} + +#endif // SB_API_VERSION >= 16 diff --git a/starboard/shared/modular/starboard_layer_posix_file_abi_wrappers.h b/starboard/shared/modular/starboard_layer_posix_file_abi_wrappers.h new file mode 100644 index 000000000000..a47269f42825 --- /dev/null +++ b/starboard/shared/modular/starboard_layer_posix_file_abi_wrappers.h @@ -0,0 +1,63 @@ +// Copyright 2024 The Cobalt Authors. All Rights Reserved. +// +// 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. + +#ifndef STARBOARD_SHARED_MODULAR_STARBOARD_LAYER_POSIX_FILE_ABI_WRAPPERS_H_ +#define STARBOARD_SHARED_MODULAR_STARBOARD_LAYER_POSIX_FILE_ABI_WRAPPERS_H_ + +#include +#include + +#include "starboard/export.h" + +// The `__abi_wrap_lseek` function converts from the musl's off_t +// type to the platform's off_t which may have different sizes as +// the definition is platform specific. +// The `__abi_wrap_read` function converts ssize_t to int32_t or +// int64_t depending on the platform. +// +// The wrapper is used by all modular builds, including Evergreen. +// +// For Evergreen-based modular builds, we will rely on the exported_symbols.cc +// mapping logic to map calls to file IO functions to `__abi_wrap_` file IO +// functions. +// +// For non-Evergreen modular builds, the Cobalt-side shared library will be +// compiled with code that remaps calls to file IO functions to `__abi_wrap_` +// file IO functions. + +// A matching type for the off_t definition in musl. + +typedef int64_t musl_off_t; + +#if SB_IS(ARCH_ARM64) || SB_IS(ARCH_X64) +typedef int64_t ssize_t; +#else +typedef int32_t ssize_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +SB_EXPORT musl_off_t __abi_wrap_lseek(int fildes, + musl_off_t offset, + int whence); + +SB_EXPORT ssize_t __abi_wrap_read(int fildes, void* buf, size_t nbyte); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // STARBOARD_SHARED_MODULAR_STARBOARD_LAYER_POSIX_FILE_ABI_WRAPPERS_H_ diff --git a/starboard/shared/modular/starboard_layer_posix_stat_abi_wrappers.cc b/starboard/shared/modular/starboard_layer_posix_stat_abi_wrappers.cc index e11f3f0084d9..4f1602662904 100644 --- a/starboard/shared/modular/starboard_layer_posix_stat_abi_wrappers.cc +++ b/starboard/shared/modular/starboard_layer_posix_stat_abi_wrappers.cc @@ -14,31 +14,41 @@ #include "starboard/shared/modular/starboard_layer_posix_stat_abi_wrappers.h" -int __abi_wrap_stat(const char* path, struct musl_stat* musl_info) { - struct stat stat_info; // The type from platform toolchain. - int retval = stat(path, &stat_info); - +int stat_helper(int retval, + struct stat* stat_info, + struct musl_stat* musl_info) { if (retval != 0 || musl_info == NULL) { return -1; } - musl_info->st_size = stat_info.st_size; - musl_info->st_mode = stat_info.st_mode; + musl_info->st_size = stat_info->st_size; + musl_info->st_mode = stat_info->st_mode; #if defined(_MSC_VER) - musl_info->st_atim.tv_sec = stat_info.st_atime; + musl_info->st_atim.tv_sec = stat_info->st_atime; musl_info->st_atim.tv_nsec = 0; - musl_info->st_mtim.tv_sec = stat_info.st_mtime; + musl_info->st_mtim.tv_sec = stat_info->st_mtime; musl_info->st_mtim.tv_nsec = 0; - musl_info->st_ctim.tv_sec = stat_info.st_ctime; + musl_info->st_ctim.tv_sec = stat_info->st_ctime; musl_info->st_ctim.tv_nsec = 0; #else - musl_info->st_atim.tv_sec = stat_info.st_atim.tv_sec; - musl_info->st_atim.tv_nsec = stat_info.st_atim.tv_nsec; - musl_info->st_mtim.tv_sec = stat_info.st_mtim.tv_sec; - musl_info->st_mtim.tv_nsec = stat_info.st_mtim.tv_nsec; - musl_info->st_ctim.tv_sec = stat_info.st_ctim.tv_sec; - musl_info->st_ctim.tv_nsec = stat_info.st_ctim.tv_nsec; + musl_info->st_atim.tv_sec = stat_info->st_atim.tv_sec; + musl_info->st_atim.tv_nsec = stat_info->st_atim.tv_nsec; + musl_info->st_mtim.tv_sec = stat_info->st_mtim.tv_sec; + musl_info->st_mtim.tv_nsec = stat_info->st_mtim.tv_nsec; + musl_info->st_ctim.tv_sec = stat_info->st_ctim.tv_sec; + musl_info->st_ctim.tv_nsec = stat_info->st_ctim.tv_nsec; #endif - return retval; } + +int __abi_wrap_fstat(int fildes, struct musl_stat* musl_info) { + struct stat stat_info; // The type from platform toolchain. + int retval = fstat(fildes, &stat_info); + return stat_helper(retval, &stat_info, musl_info); +} + +int __abi_wrap_stat(const char* path, struct musl_stat* musl_info) { + struct stat stat_info; // The type from platform toolchain. + int retval = stat(path, &stat_info); + return stat_helper(retval, &stat_info, musl_info); +} diff --git a/starboard/shared/modular/starboard_layer_posix_stat_abi_wrappers.h b/starboard/shared/modular/starboard_layer_posix_stat_abi_wrappers.h index 976e1593a9e6..cb4fe0999489 100644 --- a/starboard/shared/modular/starboard_layer_posix_stat_abi_wrappers.h +++ b/starboard/shared/modular/starboard_layer_posix_stat_abi_wrappers.h @@ -75,6 +75,8 @@ struct musl_stat { #endif }; +SB_EXPORT int __abi_wrap_fstat(int fildes, struct musl_stat* info); + SB_EXPORT int __abi_wrap_stat(const char* path, struct musl_stat* info); #ifdef __cplusplus diff --git a/starboard/shared/uwp/application_uwp.cc b/starboard/shared/uwp/application_uwp.cc index fff54a8eba23..05cf2c605761 100644 --- a/starboard/shared/uwp/application_uwp.cc +++ b/starboard/shared/uwp/application_uwp.cc @@ -24,8 +24,6 @@ #include #include -#include - #include #include #include @@ -58,8 +56,10 @@ #include "starboard/shared/uwp/watchdog_log.h" #include "starboard/shared/uwp/window_internal.h" #include "starboard/shared/win32/thread_private.h" +#include "starboard/shared/win32/time_utils.h" #include "starboard/shared/win32/video_decoder.h" #include "starboard/shared/win32/wchar_utils.h" + #include "starboard/system.h" namespace starboard { @@ -75,6 +75,7 @@ using shared::starboard::NetLogWaitForClientConnected; using shared::uwp::ApplicationUwp; using shared::uwp::RunInMainThreadAsync; using shared::uwp::WaitForResult; +using shared::win32::ConvertUsecToMillisRoundUp; using shared::win32::platformStringToString; using shared::win32::stringToPlatformString; using shared::win32::wchar_tToUTF8; @@ -335,7 +336,7 @@ std::string GetBinaryName() { void OnDeviceAdded(DeviceWatcher ^, DeviceInformation ^) { SB_LOG(INFO) << "DisplayStatusWatcher::OnDeviceAdded"; // We need delay to give time for the display initializing after connect. - usleep(15'000); + Sleep(ConvertUsecToMillisRoundUp(15'000)); MimeSupportabilityCache::GetInstance()->ClearCachedMimeSupportabilities(); diff --git a/starboard/shared/win32/posix_emu/include/fcntl.h b/starboard/shared/win32/posix_emu/include/fcntl.h index 43d70b07daa5..93f9be37b70d 100644 --- a/starboard/shared/win32/posix_emu/include/fcntl.h +++ b/starboard/shared/win32/posix_emu/include/fcntl.h @@ -16,8 +16,8 @@ #define STARBOARD_SHARED_WIN32_POSIX_EMU_INCLUDE_FCNTL_H_ #include <../ucrt/fcntl.h> // The Visual Studio version of this same file -#include // Needed for `open`, which is in fcntl.h on POSIX -#include +#include <../ucrt/sys/stat.h> +#include // Needed for `open`, which is in fcntl.h on POSIX #undef open #undef close // in unistd.h on POSIX, and handles both files and sockets diff --git a/starboard/shared/win32/posix_emu/include/sys/socket.h b/starboard/shared/win32/posix_emu/include/sys/socket.h index 88d9d5f7ebc5..39c7e5390c6c 100644 --- a/starboard/shared/win32/posix_emu/include/sys/socket.h +++ b/starboard/shared/win32/posix_emu/include/sys/socket.h @@ -15,7 +15,7 @@ #ifndef STARBOARD_SHARED_WIN32_POSIX_EMU_INCLUDE_SYS_SOCKET_H_ #define STARBOARD_SHARED_WIN32_POSIX_EMU_INCLUDE_SYS_SOCKET_H_ -#include +#include #include #include diff --git a/starboard/shared/win32/posix_emu/include/sys/stat.h b/starboard/shared/win32/posix_emu/include/sys/stat.h index f02aa0b343b8..1b40a221ea54 100644 --- a/starboard/shared/win32/posix_emu/include/sys/stat.h +++ b/starboard/shared/win32/posix_emu/include/sys/stat.h @@ -1,39 +1,44 @@ -// Copyright 2024 The Cobalt Authors. All Rights Reserved. -// -// 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. -#ifndef STARBOARD_SHARED_WIN32_POSIX_EMU_INCLUDE_SYS_STAT_H_ -#define STARBOARD_SHARED_WIN32_POSIX_EMU_INCLUDE_SYS_STAT_H_ - -#include <../ucrt/sys/stat.h> - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define S_ISLNK(mode) 0 // Windows doesn't support symbolic links -#define S_ISDIR(mode) (((mode) & _S_IFMT) == (_S_IFDIR)) -#define S_ISREG(mode) (((mode) & _S_IFMT) == (_S_IFREG)) - -typedef int mode_t; - -int sb_mkdir(const char* path, mode_t mode); -#undef mkdir -#define mkdir sb_mkdir - -#ifdef __cplusplus -} -#endif // __cplusplus - -#endif // STARBOARD_SHARED_WIN32_POSIX_EMU_INCLUDE_SYS_STAT_H_ +// Copyright 2024 The Cobalt Authors. All Rights Reserved. +// +// 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. +#ifndef STARBOARD_SHARED_WIN32_POSIX_EMU_INCLUDE_SYS_STAT_H_ +#define STARBOARD_SHARED_WIN32_POSIX_EMU_INCLUDE_SYS_STAT_H_ + +#include <../ucrt/sys/stat.h> + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define S_ISLNK(mode) 0 // Windows doesn't support symbolic links +#define S_ISDIR(mode) (((mode) & _S_IFMT) == (_S_IFDIR)) +#define S_ISREG(mode) (((mode) & _S_IFMT) == (_S_IFREG)) + +typedef int mode_t; + +// Implementation in socket.cc +int sb_fstat(int fd, struct stat* buffer); +#define fstat sb_fstat + +int sb_mkdir(const char* path, mode_t mode); +#undef mkdir +#define mkdir sb_mkdir + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // STARBOARD_SHARED_WIN32_POSIX_EMU_INCLUDE_SYS_STAT_H_ diff --git a/starboard/shared/win32/posix_emu/include/unistd.h b/starboard/shared/win32/posix_emu/include/unistd.h index 63b072fc2a37..9cce6cd430b6 100644 --- a/starboard/shared/win32/posix_emu/include/unistd.h +++ b/starboard/shared/win32/posix_emu/include/unistd.h @@ -15,14 +15,23 @@ #ifndef STARBOARD_SHARED_WIN32_POSIX_EMU_INCLUDE_UNISTD_H_ #define STARBOARD_SHARED_WIN32_POSIX_EMU_INCLUDE_UNISTD_H_ +#include + #ifdef __cplusplus extern "C" { #endif -// This function will handle both files and sockets. The implementation is -// located in socket.cc. +// The implementation of the following functions are located in socket.cc. + +// This function will handle both files and sockets. int close(int fd); +off_t sb_lseek(int fd, off_t offset, int origin); +#define lseek sb_lseek + +ssize_t sb_read(int fildes, void* buf, size_t nbyte); +#define read sb_read + int usleep(unsigned int useconds); #ifdef __cplusplus diff --git a/starboard/shared/win32/posix_emu/socket.cc b/starboard/shared/win32/posix_emu/socket.cc index b5c8a0513cd8..66eab1796661 100644 --- a/starboard/shared/win32/posix_emu/socket.cc +++ b/starboard/shared/win32/posix_emu/socket.cc @@ -20,7 +20,13 @@ #include #undef NO_ERROR // http://b/302733082#comment15 #include + #include +<<<<<<< HEAD +======= + +#include "starboard/common/log.h" +>>>>>>> ca46052289a (Add tests for POSIX fstat, lseek, and read. (#2602)) #include "starboard/types.h" static int gen_fd() { @@ -114,6 +120,8 @@ int open(const char* path, int oflag, ...) { va_start(args, oflag); int fd; mode_t mode; + // Open in binary mode because read() stops at the first 0x1A value. + oflag |= O_BINARY; if (oflag & O_CREAT) { mode = va_arg(args, mode_t); fd = _open(path, oflag, mode & MS_MODE_MASK); @@ -144,6 +152,22 @@ int close(int fd) { return _close(handle.file); } +off_t sb_lseek(int fd, off_t offset, int origin) { + FileOrSocket handle = handle_db_get(fd, false); + if (!handle.is_file) { + return -1; + } + return _lseek(handle.file, offset, origin); +} + +ssize_t sb_read(int fd, void* buf, size_t nbyte) { + FileOrSocket handle = handle_db_get(fd, false); + if (!handle.is_file) { + return -1; + } + return _read(handle.file, buf, nbyte); +} + int sb_bind(int socket, const struct sockaddr* address, socklen_t address_len) { SOCKET socket_handle = handle_db_get(socket, false).socket; if (socket_handle == INVALID_SOCKET) { @@ -298,4 +322,15 @@ int sb_fcntl(int fd, int cmd, ... /*arg*/) { return 0; } +<<<<<<< HEAD +======= +int sb_fstat(int fd, struct stat* buffer) { + FileOrSocket handle = handle_db_get(fd, false); + if (!handle.is_file) { + return -1; + } + return _fstat(handle.file, (struct _stat*)buffer); +} + +>>>>>>> ca46052289a (Add tests for POSIX fstat, lseek, and read. (#2602)) } // extern "C" diff --git a/starboard/tools/api_leak_detector/api_leak_detector.py b/starboard/tools/api_leak_detector/api_leak_detector.py index 083cd41d6d6b..ec9e0dc4ddf8 100755 --- a/starboard/tools/api_leak_detector/api_leak_detector.py +++ b/starboard/tools/api_leak_detector/api_leak_detector.py @@ -96,12 +96,14 @@ 'free', 'freeifaddrs', 'freeaddrinfo', + 'fstat', 'gettimeofday', 'getifaddrs', 'getaddrinfo', 'gmtime_r', 'inet_ntop', 'listen', + 'lseek', 'malloc', 'posix_memalign', 'recv', @@ -119,6 +121,7 @@ 'mprotect', 'msync', 'mkdir', + 'read', 'sched_yield', 'stat', 'pthread_cond_broadcast', diff --git a/starboard/xb1/platform_configuration/BUILD.gn b/starboard/xb1/platform_configuration/BUILD.gn index 85a72d667de4..6dfe2337f1ee 100644 --- a/starboard/xb1/platform_configuration/BUILD.gn +++ b/starboard/xb1/platform_configuration/BUILD.gn @@ -72,6 +72,7 @@ config("target") { "iso_stdio_wide_specifiers.lib", "mfplat.lib", "mfuuid.lib", + "oldnames.lib", "windowsapp.lib", ] ldflags += [ diff --git a/third_party/musl/include/sys/stat.h b/third_party/musl/include/sys/stat.h index 9f5abb81c935..3d6bff532b92 100644 --- a/third_party/musl/include/sys/stat.h +++ b/third_party/musl/include/sys/stat.h @@ -113,8 +113,8 @@ int lchmod(const char *, mode_t); #if _REDIR_TIME64 #if !defined(STARBOARD) __REDIR(stat, __stat_time64); -#endif // !defined(STARBOARD) __REDIR(fstat, __fstat_time64); +#endif // !defined(STARBOARD) __REDIR(lstat, __lstat_time64); __REDIR(fstatat, __fstatat_time64); __REDIR(futimens, __futimens_time64); diff --git a/third_party/musl/src/starboard/network/socket.c b/third_party/musl/src/starboard/network/socket.c index 5abe882b2921..d34533284a21 100644 --- a/third_party/musl/src/starboard/network/socket.c +++ b/third_party/musl/src/starboard/network/socket.c @@ -21,13 +21,19 @@ #include #include #include +#include #include #include #include "starboard/file.h" #include "starboard/socket.h" +<<<<<<< HEAD #include "starboard/file.h" +======= +#include "starboard/system.h" +>>>>>>> ca46052289a (Add tests for POSIX fstat, lseek, and read. (#2602)) #include "starboard/time.h" +#include "starboard/types.h" #include "../pthread/pthread.h" // Internal database function to convert SbSocket/SbFile object to @@ -149,6 +155,12 @@ static int get(int key, bool take, FileOrSocket** valuePtr) { return status; } +static SB_C_FORCE_INLINE time_t WindowsUsecToTimeT(int64_t time) { + int64_t posix_time = time - 11644473600000000ULL; + posix_time = posix_time / 1000000; + return posix_time; +} + int TranslateSocketErrnoSbToPosix(SbSocketError sbError) { switch (sbError) { case kSbSocketOk: @@ -210,6 +222,73 @@ int ConvertSocketAddressSbToPosix(const SbSocketAddress* sbAddress, struct socka // The exported POSIX APIs // +int fstat(int fildes, struct stat* buf) { + if (fildes < 0) { + errno = EBADF; + return -1; + } + + FileOrSocket* fileOrSock = NULL; + if (get(fildes, false, &fileOrSock) != 0) { + errno = EBADF; + return -1; + } + + if (fileOrSock == NULL || !fileOrSock->is_file) { + errno = EBADF; + return -1; + } + + SbFileInfo info; + if (!SbFileGetInfo(fileOrSock->file, &info)) { + return -1; + } + + buf->st_mode = 0; + if (info.is_directory) { + buf->st_mode = S_IFDIR; + } else if (info.is_symbolic_link) { + buf->st_mode = S_IFLNK; + } + buf->st_ctime = WindowsUsecToTimeT(info.creation_time); + buf->st_atime = WindowsUsecToTimeT(info.last_accessed); + buf->st_mtime = WindowsUsecToTimeT(info.last_modified); + buf->st_size = info.size; + + return 0; +} + +off_t lseek(int fildes, off_t offset, int whence) { + if (fildes < 0) { + errno = EBADF; + return -1; + } + + FileOrSocket* fileOrSock = NULL; + if (get(fildes, false, &fileOrSock) != 0) { + errno = EBADF; + return -1; + } + + if (fileOrSock == NULL || !fileOrSock->is_file) { + errno = EBADF; + return -1; + } + + SbFileWhence sbWhence; + if (whence == SEEK_SET) { + sbWhence = kSbFileFromBegin; + } else if (whence == SEEK_CUR) { + sbWhence = kSbFileFromCurrent; + } else if (whence == SEEK_END) { + sbWhence = kSbFileFromEnd; + } else { + return -1; + } + + return (off_t)SbFileSeek(fileOrSock->file, sbWhence, (int64_t)offset); +} + int open(const char* path, int oflag, ...) { bool out_created; SbFileError out_error; @@ -222,11 +301,51 @@ int open(const char* path, int oflag, ...) { memset(value, 0, sizeof(struct FileOrSocket)); value->is_file = true; - // TODO: b/302715109 map posix flags to SB file flags - int open_flags = 0; - // O_APPEND, O_ASYNC, O_CLOEXEC, O_CREAT, O_DIRECT, O_DIRECTORY, O_DSYNC - // O_EXCL, O_LARGEFILE, O_NOATIME, O_NOCTTY, O_NOFOLLOW, - // O_NONBLOCK or O_NDELAY, O_PATH, O_SYNC, O_TMPFILE, O_TRUNC + int sbFileFlags = 0; + int accessModeFlag = 0; + + if ((oflag & O_ACCMODE) == O_RDONLY) { + accessModeFlag |= kSbFileRead; + if (oflag == O_RDONLY) { + sbFileFlags = kSbFileOpenOnly; + } + } else if ((oflag & O_ACCMODE) == O_WRONLY) { + accessModeFlag |= kSbFileWrite; + oflag &= ~O_WRONLY; + } else if ((oflag & O_ACCMODE) == O_RDWR) { + accessModeFlag |= kSbFileRead | kSbFileWrite; + oflag &= ~O_RDWR; + } else { + // Applications shall specify exactly one of the first three file access + // modes. + out_error = kSbFileErrorFailed; + return -1; + } + + if (oflag & O_CREAT && oflag & O_EXCL) { + sbFileFlags = kSbFileCreateOnly; + oflag &= ~(O_CREAT | O_EXCL); + } + if (oflag & O_CREAT && oflag & O_TRUNC) { + sbFileFlags = kSbFileCreateAlways; + oflag &= ~(O_CREAT | O_TRUNC); + } + if (oflag & O_CREAT) { + sbFileFlags = kSbFileOpenAlways; + oflag &= ~O_CREAT; + } + if (oflag & O_TRUNC) { + sbFileFlags = kSbFileOpenTruncated; + oflag &= ~O_TRUNC; + } + + // SbFileOpen does not support any other combination of flags. + if (oflag || !sbFileFlags) { + out_error = kSbFileErrorFailed; + return -1; + } + + int open_flags = sbFileFlags | accessModeFlag; value->file = SbFileOpen(path, open_flags, &out_created, &out_error); if (!SbFileIsValid(value->file)){ @@ -242,6 +361,26 @@ int open(const char* path, int oflag, ...) { return result; } +ssize_t read(int fildes, void* buf, size_t nbyte) { + if (fildes < 0) { + errno = EBADF; + return -1; + } + + FileOrSocket* fileOrSock = NULL; + if (get(fildes, false, &fileOrSock) != 0) { + errno = EBADF; + return -1; + } + + if (fileOrSock == NULL || !fileOrSock->is_file) { + errno = EBADF; + return -1; + } + + return (ssize_t)SbFileRead(fileOrSock->file, buf, (int)nbyte); +} + int socket(int domain, int type, int protocol){ int address_type, socket_protocol; switch (domain){