From 5096e66d07db8041589518f8c5b0281d859d0817 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Tue, 8 Dec 2015 19:25:49 +0000 Subject: [PATCH] ART: Add FdFile::Copy() to copy data from another file. Also move utilities for inspecting file magic numbers to base/file_magic.{h,cc} and drop the unused IsOatMagic(). Change-Id: I2cc4dd18a5e8b9738fb386c8057faad3722bdd68 --- runtime/Android.mk | 1 + runtime/base/file_magic.cc | 58 ++++++++++++++++++++++++++++++++++ runtime/base/file_magic.h | 36 +++++++++++++++++++++ runtime/base/unix_file/fd_file.cc | 56 ++++++++++++++++++++++++++++++++ runtime/base/unix_file/fd_file.h | 15 +++++---- runtime/base/unix_file/fd_file_test.cc | 30 ++++++++++++++++++ runtime/dex_file.cc | 21 +----------- runtime/utils.cc | 15 --------- runtime/utils.h | 5 --- 9 files changed, 191 insertions(+), 46 deletions(-) create mode 100644 runtime/base/file_magic.cc create mode 100644 runtime/base/file_magic.h diff --git a/runtime/Android.mk b/runtime/Android.mk index 571a2f5d6..74cc89911 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -27,6 +27,7 @@ LIBART_COMMON_SRC_FILES := \ base/arena_allocator.cc \ base/arena_bit_vector.cc \ base/bit_vector.cc \ + base/file_magic.cc \ base/hex_dump.cc \ base/logging.cc \ base/mutex.cc \ diff --git a/runtime/base/file_magic.cc b/runtime/base/file_magic.cc new file mode 100644 index 000000000..97563382a --- /dev/null +++ b/runtime/base/file_magic.cc @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 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 "file_magic.h" + +#include +#include +#include + +#include "base/logging.h" +#include "dex_file.h" +#include "stringprintf.h" + +namespace art { + +ScopedFd OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* error_msg) { + CHECK(magic != nullptr); + ScopedFd fd(open(filename, O_RDONLY, 0)); + if (fd.get() == -1) { + *error_msg = StringPrintf("Unable to open '%s' : %s", filename, strerror(errno)); + return ScopedFd(); + } + int n = TEMP_FAILURE_RETRY(read(fd.get(), magic, sizeof(*magic))); + if (n != sizeof(*magic)) { + *error_msg = StringPrintf("Failed to find magic in '%s'", filename); + return ScopedFd(); + } + if (lseek(fd.get(), 0, SEEK_SET) != 0) { + *error_msg = StringPrintf("Failed to seek to beginning of file '%s' : %s", filename, + strerror(errno)); + return ScopedFd(); + } + return fd; +} + +bool IsZipMagic(uint32_t magic) { + return (('P' == ((magic >> 0) & 0xff)) && + ('K' == ((magic >> 8) & 0xff))); +} + +bool IsDexMagic(uint32_t magic) { + return DexFile::IsMagicValid(reinterpret_cast(&magic)); +} + +} // namespace art diff --git a/runtime/base/file_magic.h b/runtime/base/file_magic.h new file mode 100644 index 000000000..f7e4bad16 --- /dev/null +++ b/runtime/base/file_magic.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ART_RUNTIME_BASE_FILE_MAGIC_H_ +#define ART_RUNTIME_BASE_FILE_MAGIC_H_ + +#include +#include + +#include "ScopedFd.h" + +namespace art { + +// Open file and read magic number +ScopedFd OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* error_msg); + +// Check whether the given magic matches a known file type. +bool IsZipMagic(uint32_t magic); +bool IsDexMagic(uint32_t magic); + +} // namespace art + +#endif // ART_RUNTIME_BASE_FILE_MAGIC_H_ diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc index 07cadc48d..78bc3d5f9 100644 --- a/runtime/base/unix_file/fd_file.cc +++ b/runtime/base/unix_file/fd_file.cc @@ -17,12 +17,22 @@ #include "base/unix_file/fd_file.h" #include +#include #include #include #include #include "base/logging.h" +// Includes needed for FdFile::Copy(). +#ifdef __linux__ +#include +#else +#include +#include "base/stl_util.h" +#include "globals.h" +#endif + namespace unix_file { FdFile::FdFile() : guard_state_(GuardState::kClosed), fd_(-1), auto_close_(true) { @@ -222,6 +232,52 @@ bool FdFile::WriteFully(const void* buffer, size_t byte_count) { return true; } +bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) { + off_t off = static_cast(offset); + off_t sz = static_cast(size); + if (offset < 0 || static_cast(off) != offset || + size < 0 || static_cast(sz) != size || + sz > std::numeric_limits::max() - off) { + errno = EINVAL; + return false; + } + if (size == 0) { + return true; + } +#ifdef __linux__ + // Use sendfile(), available for files since linux kernel 2.6.33. + off_t end = off + sz; + while (off != end) { + int result = TEMP_FAILURE_RETRY( + sendfile(Fd(), input_file->Fd(), &off, end - off)); + if (result == -1) { + return false; + } + // Ignore the number of bytes in `result`, sendfile() already updated `off`. + } +#else + if (lseek(input_file->Fd(), off, SEEK_SET) != off) { + return false; + } + constexpr size_t kMaxBufferSize = 4 * ::art::kPageSize; + const size_t buffer_size = std::min(size, kMaxBufferSize); + art::UniqueCPtr buffer(malloc(buffer_size)); + if (buffer == nullptr) { + errno = ENOMEM; + return false; + } + while (size != 0) { + size_t chunk_size = std::min(buffer_size, size); + if (!input_file->ReadFully(buffer.get(), chunk_size) || + !WriteFully(buffer.get(), chunk_size)) { + return false; + } + size -= chunk_size; + } +#endif + return true; +} + void FdFile::Erase() { TEMP_FAILURE_RETRY(SetLength(0)); TEMP_FAILURE_RETRY(Flush()); diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h index f47368b18..231a1ab14 100644 --- a/runtime/base/unix_file/fd_file.h +++ b/runtime/base/unix_file/fd_file.h @@ -50,12 +50,12 @@ class FdFile : public RandomAccessFile { bool Open(const std::string& file_path, int flags, mode_t mode); // RandomAccessFile API. - virtual int Close() WARN_UNUSED; - virtual int64_t Read(char* buf, int64_t byte_count, int64_t offset) const WARN_UNUSED; - virtual int SetLength(int64_t new_length) WARN_UNUSED; - virtual int64_t GetLength() const; - virtual int64_t Write(const char* buf, int64_t byte_count, int64_t offset) WARN_UNUSED; - virtual int Flush() WARN_UNUSED; + int Close() OVERRIDE WARN_UNUSED; + int64_t Read(char* buf, int64_t byte_count, int64_t offset) const OVERRIDE WARN_UNUSED; + int SetLength(int64_t new_length) OVERRIDE WARN_UNUSED; + int64_t GetLength() const OVERRIDE; + int64_t Write(const char* buf, int64_t byte_count, int64_t offset) OVERRIDE WARN_UNUSED; + int Flush() OVERRIDE WARN_UNUSED; // Short for SetLength(0); Flush(); Close(); void Erase(); @@ -77,6 +77,9 @@ class FdFile : public RandomAccessFile { bool PreadFully(void* buffer, size_t byte_count, size_t offset) WARN_UNUSED; bool WriteFully(const void* buffer, size_t byte_count) WARN_UNUSED; + // Copy data from another file. + bool Copy(FdFile* input_file, int64_t offset, int64_t size); + // This enum is public so that we can define the << operator over it. enum class GuardState { kBase, // Base, file has not been flushed or closed. diff --git a/runtime/base/unix_file/fd_file_test.cc b/runtime/base/unix_file/fd_file_test.cc index 388f71792..ecf607c89 100644 --- a/runtime/base/unix_file/fd_file_test.cc +++ b/runtime/base/unix_file/fd_file_test.cc @@ -110,4 +110,34 @@ TEST_F(FdFileTest, ReadFullyWithOffset) { ASSERT_EQ(file.Close(), 0); } +TEST_F(FdFileTest, Copy) { + art::ScratchFile src_tmp; + FdFile src; + ASSERT_TRUE(src.Open(src_tmp.GetFilename(), O_RDWR)); + ASSERT_GE(src.Fd(), 0); + ASSERT_TRUE(src.IsOpened()); + + char src_data[] = "Some test data."; + ASSERT_TRUE(src.WriteFully(src_data, sizeof(src_data))); // Including the zero terminator. + ASSERT_EQ(0, src.Flush()); + ASSERT_EQ(static_cast(sizeof(src_data)), src.GetLength()); + + art::ScratchFile dest_tmp; + FdFile dest; + ASSERT_TRUE(dest.Open(src_tmp.GetFilename(), O_RDWR)); + ASSERT_GE(dest.Fd(), 0); + ASSERT_TRUE(dest.IsOpened()); + + ASSERT_TRUE(dest.Copy(&src, 0, sizeof(src_data))); + ASSERT_EQ(0, dest.Flush()); + ASSERT_EQ(static_cast(sizeof(src_data)), dest.GetLength()); + + char check_data[sizeof(src_data)]; + ASSERT_TRUE(dest.PreadFully(check_data, sizeof(src_data), 0u)); + CHECK_EQ(0, memcmp(check_data, src_data, sizeof(src_data))); + + ASSERT_EQ(0, dest.Close()); + ASSERT_EQ(0, src.Close()); +} + } // namespace unix_file diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 4163e2efd..30d921afe 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -29,6 +29,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" +#include "base/file_magic.h" #include "base/hash_map.h" #include "base/logging.h" #include "base/stl_util.h" @@ -62,26 +63,6 @@ namespace art { const uint8_t DexFile::kDexMagic[] = { 'd', 'e', 'x', '\n' }; const uint8_t DexFile::kDexMagicVersion[] = { '0', '3', '5', '\0' }; -static int OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* error_msg) { - CHECK(magic != nullptr); - ScopedFd fd(open(filename, O_RDONLY, 0)); - if (fd.get() == -1) { - *error_msg = StringPrintf("Unable to open '%s' : %s", filename, strerror(errno)); - return -1; - } - int n = TEMP_FAILURE_RETRY(read(fd.get(), magic, sizeof(*magic))); - if (n != sizeof(*magic)) { - *error_msg = StringPrintf("Failed to find magic in '%s'", filename); - return -1; - } - if (lseek(fd.get(), 0, SEEK_SET) != 0) { - *error_msg = StringPrintf("Failed to seek to beginning of file '%s' : %s", filename, - strerror(errno)); - return -1; - } - return fd.release(); -} - bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg) { CHECK(checksum != nullptr); uint32_t magic; diff --git a/runtime/utils.cc b/runtime/utils.cc index 68db7e3a7..eddc3a417 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1392,21 +1392,6 @@ std::string GetSystemImageFilename(const char* location, const InstructionSet is return filename; } -bool IsZipMagic(uint32_t magic) { - return (('P' == ((magic >> 0) & 0xff)) && - ('K' == ((magic >> 8) & 0xff))); -} - -bool IsDexMagic(uint32_t magic) { - return DexFile::IsMagicValid(reinterpret_cast(&magic)); -} - -bool IsOatMagic(uint32_t magic) { - return (memcmp(reinterpret_cast(magic), - OatHeader::kOatMagic, - sizeof(OatHeader::kOatMagic)) == 0); -} - bool Exec(std::vector& arg_vector, std::string* error_msg) { const std::string command_line(Join(arg_vector, ' ')); diff --git a/runtime/utils.h b/runtime/utils.h index 8b7941a1b..5b9e96391 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -273,11 +273,6 @@ std::string GetDalvikCacheFilenameOrDie(const char* file_location, // Returns the system location for an image std::string GetSystemImageFilename(const char* location, InstructionSet isa); -// Check whether the given magic matches a known file type. -bool IsZipMagic(uint32_t magic); -bool IsDexMagic(uint32_t magic); -bool IsOatMagic(uint32_t magic); - // Wrapper on fork/execv to run a command in a subprocess. bool Exec(std::vector& arg_vector, std::string* error_msg); -- 2.11.0