diff --git a/util/mac/xattr.cc b/util/mac/xattr.cc new file mode 100644 index 00000000..750a056d --- /dev/null +++ b/util/mac/xattr.cc @@ -0,0 +1,142 @@ +// Copyright 2014 The Crashpad 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 "util/mac/xattr.h" + +#include +#include + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/strings/string_number_conversions.h" + +namespace crashpad { + +bool ReadXattr(const base::FilePath& file, + const base::StringPiece& name, + std::string* value) { + // First get the size of the attribute value. + ssize_t buffer_size = getxattr(file.value().c_str(), name.data(), nullptr, + 0, 0, 0); + if (buffer_size < 0) { + PLOG(ERROR) << "getxattr size " << name << " on file " << file.value(); + return false; + } + + // Resize the buffer and read into it. + value->resize(buffer_size); + ssize_t bytes_read = getxattr(file.value().c_str(), name.data(), + &(*value)[0], value->size(), + 0, 0); + if (bytes_read < 0) { + PLOG(ERROR) << "getxattr " << name << " on file " << file.value(); + return false; + } + DCHECK_EQ(bytes_read, buffer_size); + + return true; +} + +bool WriteXattr(const base::FilePath& file, + const base::StringPiece& name, + const std::string& value) { + int rv = setxattr(file.value().c_str(), name.data(), value.c_str(), + value.length(), 0, 0); + PLOG_IF(ERROR, rv != 0) << "setxattr " << name << " on file " + << file.value(); + return rv == 0; +} + +bool ReadXattrBool(const base::FilePath& file, + const base::StringPiece& name, + bool* value) { + std::string tmp; + if (!ReadXattr(file, name, &tmp)) + return false; + if (tmp == "1") { + *value = true; + return true; + } else if (tmp == "0") { + *value = false; + return true; + } else { + LOG(ERROR) << "ReadXattrBool " << name << " on file " << file.value() + << " could not be interpreted as boolean"; + return false; + } +} + +bool WriteXattrBool(const base::FilePath& file, + const base::StringPiece& name, + bool value) { + return WriteXattr(file, name, (value ? "1" : "0")); +} + +bool ReadXattrInt(const base::FilePath& file, + const base::StringPiece& name, + int* value) { + std::string tmp; + if (!ReadXattr(file, name, &tmp)) + return false; + if (!base::StringToInt(tmp, value)) { + LOG(ERROR) << "ReadXattrInt " << name << " on file " << file.value() + << " could not be converted to an int"; + return false; + } + return true; +} + +bool WriteXattrInt(const base::FilePath& file, + const base::StringPiece& name, + int value) { + std::string tmp = base::StringPrintf("%d", value); + return WriteXattr(file, name, tmp); +} + +bool ReadXattrTimeT(const base::FilePath& file, + const base::StringPiece& name, + time_t* value) { + // time_t on OS X is defined as a long, but it will be read into an + // int64_t here, since there is no string conversion method for long. + std::string tmp; + if (!ReadXattr(file, name, &tmp)) + return false; + + int64_t encoded_value; + if (!base::StringToInt64(tmp, &encoded_value)) { + LOG(ERROR) << "ReadXattrTimeT " << name << " on file " << file.value() + << " could not be converted to an int"; + return false; + } + + *value = base::saturated_cast(encoded_value); + if (!base::IsValueInRangeForNumericType(encoded_value)) { + LOG(ERROR) << "ReadXattrTimeT " << name << " on file " << file.value() + << " read over-sized value and will saturate"; + return false; + } + + return true; +} + +bool WriteXattrTimeT(const base::FilePath& file, + const base::StringPiece& name, + time_t value) { + std::string tmp = base::StringPrintf("%ld", value); + return WriteXattr(file, name, tmp); +} + +} // namespace crashpad diff --git a/util/mac/xattr.h b/util/mac/xattr.h new file mode 100644 index 00000000..b816f390 --- /dev/null +++ b/util/mac/xattr.h @@ -0,0 +1,86 @@ +// Copyright 2014 The Crashpad 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 CRASHPAD_UTIL_MAC_XATTR_H_ +#define CRASHPAD_UTIL_MAC_XATTR_H_ + +#include + +#include + +#include "base/files/file_path.h" +#include "base/strings/string_piece.h" + +namespace crashpad { + +//! \brief Reads an extended attribute on a file. +//! +//! \param[in] file The path to the file. +//! \param[in] name The name of the extended attribute to read. +//! \param[out] value The value of the attribute. +//! +//! \return `true` if the read was successful, with \a value filled in. `false` +//! on error, with a message logged. +bool ReadXattr(const base::FilePath& file, + const base::StringPiece& name, + std::string* value); + +//! \brief Writes an extended attribute on a file. +//! +//! \param[in] file The path to the file. +//! \param[in] name The name of the extended attribute to write. +//! \param[in] value The value of the attribute. +//! +//! \return `true` if the write was successful. `false` on error, with a message +//! logged. +bool WriteXattr(const base::FilePath& file, + const base::StringPiece& name, + const std::string& value); + +//! \copydoc ReadXattr +//! +//! Only the values `"0"` and `"1"`, for `false` and `true` respectively, are +//! valid conversions. +bool ReadXattrBool(const base::FilePath& file, + const base::StringPiece& name, + bool* value); + +//! \copydoc WriteXattr +bool WriteXattrBool(const base::FilePath& file, + const base::StringPiece& name, + bool value); + +//! \copydoc ReadXattr +bool ReadXattrInt(const base::FilePath& file, + const base::StringPiece& name, + int* value); + +//! \copydoc WriteXattr +bool WriteXattrInt(const base::FilePath& file, + const base::StringPiece& name, + int value); + +//! \copydoc ReadXattr +bool ReadXattrTimeT(const base::FilePath& file, + const base::StringPiece& name, + time_t* value); + +//! \copydoc WriteXattr +bool WriteXattrTimeT(const base::FilePath& file, + const base::StringPiece& name, + time_t value); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MAC_XATTR_H_ diff --git a/util/mac/xattr_test.cc b/util/mac/xattr_test.cc new file mode 100644 index 00000000..e6bbf7b1 --- /dev/null +++ b/util/mac/xattr_test.cc @@ -0,0 +1,123 @@ +// Copyright 2014 The Crashpad 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 "util/mac/xattr.h" + +#include +#include +#include + +#include + +#include "base/files/scoped_file.h" +#include "base/posix/eintr_wrapper.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "util/test/errors.h" + +namespace crashpad { +namespace test { +namespace { + +class Xattr : public testing::Test { + public: + // testing::Test: + + void SetUp() override { + path_ = base::FilePath( + base::StringPrintf("/tmp/com.googlecode.crashpad.test.xattr.%d.%p", + getpid(), this)); + // TODO(rsesek): This should use something like ScopedTempDir. + base::ScopedFD tmp(HANDLE_EINTR( + open(path_.value().c_str(), O_CREAT | O_TRUNC, 0644))); + EXPECT_GE(tmp.get(), 0) << ErrnoMessage("open"); + } + + void TearDown() override { + EXPECT_EQ(0, unlink(path_.value().c_str())) << ErrnoMessage("unlink"); + } + + base::FilePath path() { return path_; } + + private: + base::FilePath path_; +}; + +const char kKey[] = "com.google.crashpad.test"; + +TEST_F(Xattr, ReadNonExistentXattr) { + std::string value; + EXPECT_FALSE(ReadXattr(path(), kKey, &value)); +} + +TEST_F(Xattr, WriteAndReadString) { + std::string value = "hello world"; + EXPECT_TRUE(WriteXattr(path(), kKey, value)); + + std::string actual; + EXPECT_TRUE(ReadXattr(path(), kKey, &actual)); + EXPECT_EQ(value, actual); +} + +TEST_F(Xattr, WriteAndReadVeryLongString) { + std::string value(533, 'A'); + EXPECT_TRUE(WriteXattr(path(), kKey, value)); + + std::string actual; + EXPECT_TRUE(ReadXattr(path(), kKey, &actual)); + EXPECT_EQ(value, actual); +} + +TEST_F(Xattr, WriteAndReadBool) { + EXPECT_TRUE(WriteXattrBool(path(), kKey, true)); + bool actual = false; + EXPECT_TRUE(ReadXattrBool(path(), kKey, &actual)); + EXPECT_TRUE(actual); + + EXPECT_TRUE(WriteXattrBool(path(), kKey, false)); + EXPECT_TRUE(ReadXattrBool(path(), kKey, &actual)); + EXPECT_FALSE(actual); +} + +TEST_F(Xattr, WriteAndReadInt) { + int expected = 42; + int actual; + + EXPECT_TRUE(WriteXattrInt(path(), kKey, expected)); + EXPECT_TRUE(ReadXattrInt(path(), kKey, &actual)); + EXPECT_EQ(expected, actual); + + expected = std::numeric_limits::max(); + EXPECT_TRUE(WriteXattrInt(path(), kKey, expected)); + EXPECT_TRUE(ReadXattrInt(path(), kKey, &actual)); + EXPECT_EQ(expected, actual); +} + +TEST_F(Xattr, WriteAndReadTimeT) { + time_t expected = time(nullptr); + time_t actual; + + EXPECT_TRUE(WriteXattrTimeT(path(), kKey, expected)); + EXPECT_TRUE(ReadXattrTimeT(path(), kKey, &actual)); + EXPECT_EQ(expected, actual); + + expected = std::numeric_limits::max(); + EXPECT_TRUE(WriteXattrTimeT(path(), kKey, expected)); + EXPECT_TRUE(ReadXattrTimeT(path(), kKey, &actual)); + EXPECT_EQ(expected, actual); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/util/util.gyp b/util/util.gyp index 9ceec2be..14b3cd2f 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -42,6 +42,8 @@ 'mac/mac_util.h', 'mac/service_management.cc', 'mac/service_management.h', + 'mac/xattr.cc', + 'mac/xattr.h', 'mach/child_port.defs', 'mach/child_port_handshake.cc', 'mach/child_port_handshake.h', @@ -245,6 +247,7 @@ 'mac/launchd_test.mm', 'mac/mac_util_test.mm', 'mac/service_management_test.mm', + 'mac/xattr_test.cc', 'mach/child_port_handshake_test.cc', 'mach/child_port_server_test.cc', 'mach/composite_mach_message_server_test.cc',