Merge "Switch fastboot/init/libprocessgroup to std::this_thread::sleep_for."
diff --git a/init/action.cpp b/init/action.cpp
index a12f225..ccc18cf 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -118,16 +118,14 @@
Timer t;
int result = command.InvokeFunc();
- double duration_ms = t.duration() * 1000;
- // Any action longer than 50ms will be warned to user as slow operation
- if (duration_ms > 50.0 ||
- android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
+ // TODO: this should probably be changed to "if (failed || took a long time)"...
+ if (android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
std::string trigger_name = BuildTriggersString();
std::string cmd_str = command.BuildCommandString();
std::string source = command.BuildSourceString();
LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << source
- << " returned " << result << " took " << duration_ms << "ms.";
+ << " returned " << result << " took " << t.duration() << "s";
}
}
@@ -249,7 +247,9 @@
result += event_trigger_;
result += ' ';
}
- result.pop_back();
+ if (!result.empty()) {
+ result.pop_back();
+ }
return result;
}
diff --git a/libappfuse/Android.bp b/libappfuse/Android.bp
index 8b46154..f729faf 100644
--- a/libappfuse/Android.bp
+++ b/libappfuse/Android.bp
@@ -15,12 +15,20 @@
name: "libappfuse",
defaults: ["libappfuse_defaults"],
export_include_dirs: ["include"],
- srcs: ["FuseBuffer.cc", "FuseBridgeLoop.cc"]
+ srcs: [
+ "FuseAppLoop.cc",
+ "FuseBuffer.cc",
+ "FuseBridgeLoop.cc",
+ ]
}
cc_test {
name: "libappfuse_test",
defaults: ["libappfuse_defaults"],
shared_libs: ["libappfuse"],
- srcs: ["tests/FuseBridgeLoopTest.cc", "tests/FuseBufferTest.cc"]
+ srcs: [
+ "tests/FuseAppLoopTest.cc",
+ "tests/FuseBridgeLoopTest.cc",
+ "tests/FuseBufferTest.cc",
+ ]
}
diff --git a/libappfuse/FuseAppLoop.cc b/libappfuse/FuseAppLoop.cc
new file mode 100644
index 0000000..a31880e
--- /dev/null
+++ b/libappfuse/FuseAppLoop.cc
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2016 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 specic language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libappfuse/FuseAppLoop.h"
+
+#include <sys/stat.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fuse {
+
+namespace {
+
+void HandleLookUp(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ // AppFuse does not support directory structure now.
+ // It can lookup only files under the mount point.
+ if (buffer->request.header.nodeid != FUSE_ROOT_ID) {
+ LOG(ERROR) << "Nodeid is not FUSE_ROOT_ID.";
+ buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
+ return;
+ }
+
+ // Ensure that the filename ends with 0.
+ const size_t filename_length =
+ buffer->request.header.len - sizeof(fuse_in_header);
+ if (buffer->request.lookup_name[filename_length - 1] != 0) {
+ LOG(ERROR) << "File name does not end with 0.";
+ buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
+ return;
+ }
+
+ const uint64_t inode =
+ static_cast<uint64_t>(atol(buffer->request.lookup_name));
+ if (inode == 0 || inode == LONG_MAX) {
+ LOG(ERROR) << "Invalid filename";
+ buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
+ return;
+ }
+
+ const int64_t size = callback->OnGetSize(inode);
+ if (size < 0) {
+ buffer->response.Reset(0, size, buffer->request.header.unique);
+ return;
+ }
+
+ buffer->response.Reset(sizeof(fuse_entry_out), 0,
+ buffer->request.header.unique);
+ buffer->response.entry_out.nodeid = inode;
+ buffer->response.entry_out.attr_valid = 10;
+ buffer->response.entry_out.entry_valid = 10;
+ buffer->response.entry_out.attr.ino = inode;
+ buffer->response.entry_out.attr.mode = S_IFREG | 0777;
+ buffer->response.entry_out.attr.size = size;
+}
+
+void HandleGetAttr(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ const uint64_t nodeid = buffer->request.header.nodeid;
+ int64_t size;
+ uint32_t mode;
+ if (nodeid == FUSE_ROOT_ID) {
+ size = 0;
+ mode = S_IFDIR | 0777;
+ } else {
+ size = callback->OnGetSize(buffer->request.header.nodeid);
+ if (size < 0) {
+ buffer->response.Reset(0, size, buffer->request.header.unique);
+ return;
+ }
+ mode = S_IFREG | 0777;
+ }
+
+ buffer->response.Reset(sizeof(fuse_attr_out), 0,
+ buffer->request.header.unique);
+ buffer->response.attr_out.attr_valid = 10;
+ buffer->response.attr_out.attr.ino = nodeid;
+ buffer->response.attr_out.attr.mode = mode;
+ buffer->response.attr_out.attr.size = size;
+}
+
+void HandleOpen(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ const int32_t file_handle = callback->OnOpen(buffer->request.header.nodeid);
+ if (file_handle < 0) {
+ buffer->response.Reset(0, file_handle, buffer->request.header.unique);
+ return;
+ }
+ buffer->response.Reset(sizeof(fuse_open_out), kFuseSuccess,
+ buffer->request.header.unique);
+ buffer->response.open_out.fh = file_handle;
+}
+
+void HandleFsync(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ buffer->response.Reset(0, callback->OnFsync(buffer->request.header.nodeid),
+ buffer->request.header.unique);
+}
+
+void HandleRelease(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ buffer->response.Reset(0, callback->OnRelease(buffer->request.header.nodeid),
+ buffer->request.header.unique);
+}
+
+void HandleRead(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ const uint64_t unique = buffer->request.header.unique;
+ const uint64_t nodeid = buffer->request.header.nodeid;
+ const uint64_t offset = buffer->request.read_in.offset;
+ const uint32_t size = buffer->request.read_in.size;
+
+ if (size > kFuseMaxRead) {
+ buffer->response.Reset(0, -EINVAL, buffer->request.header.unique);
+ return;
+ }
+
+ const int32_t read_size = callback->OnRead(nodeid, offset, size,
+ buffer->response.read_data);
+ if (read_size < 0) {
+ buffer->response.Reset(0, read_size, buffer->request.header.unique);
+ return;
+ }
+
+ buffer->response.ResetHeader(read_size, kFuseSuccess, unique);
+}
+
+void HandleWrite(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ const uint64_t unique = buffer->request.header.unique;
+ const uint64_t nodeid = buffer->request.header.nodeid;
+ const uint64_t offset = buffer->request.write_in.offset;
+ const uint32_t size = buffer->request.write_in.size;
+
+ if (size > kFuseMaxWrite) {
+ buffer->response.Reset(0, -EINVAL, buffer->request.header.unique);
+ return;
+ }
+
+ const int32_t write_size = callback->OnWrite(nodeid, offset, size,
+ buffer->request.write_data);
+ if (write_size < 0) {
+ buffer->response.Reset(0, write_size, buffer->request.header.unique);
+ return;
+ }
+
+ buffer->response.Reset(sizeof(fuse_write_out), kFuseSuccess, unique);
+ buffer->response.write_out.size = write_size;
+}
+
+} // namespace
+
+bool StartFuseAppLoop(int raw_fd, FuseAppLoopCallback* callback) {
+ base::unique_fd fd(raw_fd);
+ FuseBuffer buffer;
+
+ LOG(DEBUG) << "Start fuse loop.";
+ while (callback->IsActive()) {
+ if (!buffer.request.Read(fd)) {
+ return false;
+ }
+
+ const uint32_t opcode = buffer.request.header.opcode;
+ LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
+ switch (opcode) {
+ case FUSE_FORGET:
+ // Do not reply to FUSE_FORGET.
+ continue;
+
+ case FUSE_LOOKUP:
+ HandleLookUp(&buffer, callback);
+ break;
+
+ case FUSE_GETATTR:
+ HandleGetAttr(&buffer, callback);
+ break;
+
+ case FUSE_OPEN:
+ HandleOpen(&buffer, callback);
+ break;
+
+ case FUSE_READ:
+ HandleRead(&buffer, callback);
+ break;
+
+ case FUSE_WRITE:
+ HandleWrite(&buffer, callback);
+ break;
+
+ case FUSE_RELEASE:
+ HandleRelease(&buffer, callback);
+ break;
+
+ case FUSE_FSYNC:
+ HandleFsync(&buffer, callback);
+ break;
+
+ default:
+ buffer.HandleNotImpl();
+ break;
+ }
+
+ if (!buffer.response.Write(fd)) {
+ LOG(ERROR) << "Failed to write a response to the device.";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace fuse
+} // namespace android
diff --git a/libappfuse/FuseBridgeLoop.cc b/libappfuse/FuseBridgeLoop.cc
index 332556d..acb963c 100644
--- a/libappfuse/FuseBridgeLoop.cc
+++ b/libappfuse/FuseBridgeLoop.cc
@@ -25,14 +25,15 @@
int raw_dev_fd, int raw_proxy_fd, FuseBridgeLoop::Callback* callback) {
base::unique_fd dev_fd(raw_dev_fd);
base::unique_fd proxy_fd(raw_proxy_fd);
+ fuse::FuseBuffer buffer;
LOG(DEBUG) << "Start fuse loop.";
while (true) {
- if (!buffer_.request.Read(dev_fd)) {
+ if (!buffer.request.Read(dev_fd)) {
return false;
}
- const uint32_t opcode = buffer_.request.header.opcode;
+ const uint32_t opcode = buffer.request.header.opcode;
LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
switch (opcode) {
case FUSE_FORGET:
@@ -45,27 +46,27 @@
case FUSE_READ:
case FUSE_WRITE:
case FUSE_RELEASE:
- case FUSE_FLUSH:
- if (!buffer_.request.Write(proxy_fd)) {
+ case FUSE_FSYNC:
+ if (!buffer.request.Write(proxy_fd)) {
LOG(ERROR) << "Failed to write a request to the proxy.";
return false;
}
- if (!buffer_.response.Read(proxy_fd)) {
+ if (!buffer.response.Read(proxy_fd)) {
LOG(ERROR) << "Failed to read a response from the proxy.";
return false;
}
break;
case FUSE_INIT:
- buffer_.HandleInit();
+ buffer.HandleInit();
break;
default:
- buffer_.HandleNotImpl();
+ buffer.HandleNotImpl();
break;
}
- if (!buffer_.response.Write(dev_fd)) {
+ if (!buffer.response.Write(dev_fd)) {
LOG(ERROR) << "Failed to write a response to the device.";
return false;
}
@@ -76,4 +77,12 @@
}
}
+namespace fuse {
+
+bool StartFuseBridgeLoop(
+ int raw_dev_fd, int raw_proxy_fd, FuseBridgeLoopCallback* callback) {
+ return FuseBridgeLoop().Start(raw_dev_fd, raw_proxy_fd, callback);
+}
+
+} // namespace fuse
} // namespace android
diff --git a/libappfuse/FuseBuffer.cc b/libappfuse/FuseBuffer.cc
index 45280a5..74fe756 100644
--- a/libappfuse/FuseBuffer.cc
+++ b/libappfuse/FuseBuffer.cc
@@ -21,15 +21,22 @@
#include <unistd.h>
#include <algorithm>
+#include <type_traits>
#include <android-base/logging.h>
#include <android-base/macros.h>
namespace android {
+namespace fuse {
-template <typename T, typename Header>
-bool FuseMessage<T, Header>::CheckHeaderLength() const {
- if (sizeof(Header) <= header.len && header.len <= sizeof(T)) {
+static_assert(
+ std::is_standard_layout<FuseBuffer>::value,
+ "FuseBuffer must be standard layout union.");
+
+template <typename T>
+bool FuseMessage<T>::CheckHeaderLength() const {
+ const auto& header = static_cast<const T*>(this)->header;
+ if (sizeof(header) <= header.len && header.len <= sizeof(T)) {
return true;
} else {
LOG(ERROR) << "Packet size is invalid=" << header.len;
@@ -37,27 +44,29 @@
}
}
-template <typename T, typename Header>
-bool FuseMessage<T, Header>::CheckResult(
+template <typename T>
+bool FuseMessage<T>::CheckResult(
int result, const char* operation_name) const {
+ const auto& header = static_cast<const T*>(this)->header;
if (result >= 0 && static_cast<uint32_t>(result) == header.len) {
return true;
} else {
PLOG(ERROR) << "Failed to " << operation_name
- << " a packet from FD. result=" << result << " header.len="
+ << " a packet. result=" << result << " header.len="
<< header.len;
return false;
}
}
-template <typename T, typename Header>
-bool FuseMessage<T, Header>::Read(int fd) {
+template <typename T>
+bool FuseMessage<T>::Read(int fd) {
const ssize_t result = TEMP_FAILURE_RETRY(::read(fd, this, sizeof(T)));
return CheckHeaderLength() && CheckResult(result, "read");
}
-template <typename T, typename Header>
-bool FuseMessage<T, Header>::Write(int fd) const {
+template <typename T>
+bool FuseMessage<T>::Write(int fd) const {
+ const auto& header = static_cast<const T*>(this)->header;
if (!CheckHeaderLength()) {
return false;
}
@@ -65,8 +74,16 @@
return CheckResult(result, "write");
}
-template struct FuseMessage<FuseRequest, fuse_in_header>;
-template struct FuseMessage<FuseResponse, fuse_out_header>;
+template class FuseMessage<FuseRequest>;
+template class FuseMessage<FuseResponse>;
+
+void FuseRequest::Reset(
+ uint32_t data_length, uint32_t opcode, uint64_t unique) {
+ memset(this, 0, sizeof(fuse_in_header) + data_length);
+ header.len = sizeof(fuse_in_header) + data_length;
+ header.opcode = opcode;
+ header.unique = unique;
+}
void FuseResponse::ResetHeader(
uint32_t data_length, int32_t error, uint64_t unique) {
@@ -133,4 +150,5 @@
response.Reset(0, -ENOSYS, unique);
}
+} // namespace fuse
} // namespace android
diff --git a/libappfuse/include/libappfuse/FuseAppLoop.h b/libappfuse/include/libappfuse/FuseAppLoop.h
new file mode 100644
index 0000000..c3edfcc
--- /dev/null
+++ b/libappfuse/include/libappfuse/FuseAppLoop.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 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 specic language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
+#define ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
+
+#include "libappfuse/FuseBuffer.h"
+
+namespace android {
+namespace fuse {
+
+class FuseAppLoopCallback {
+ public:
+ virtual bool IsActive() = 0;
+ virtual int64_t OnGetSize(uint64_t inode) = 0;
+ virtual int32_t OnFsync(uint64_t inode) = 0;
+ virtual int32_t OnWrite(
+ uint64_t inode, uint64_t offset, uint32_t size, const void* data) = 0;
+ virtual int32_t OnRead(
+ uint64_t inode, uint64_t offset, uint32_t size, void* data) = 0;
+ virtual int32_t OnOpen(uint64_t inode) = 0;
+ virtual int32_t OnRelease(uint64_t inode) = 0;
+ virtual ~FuseAppLoopCallback() = default;
+};
+
+bool StartFuseAppLoop(int fd, FuseAppLoopCallback* callback);
+
+} // namespace fuse
+} // namespace android
+
+#endif // ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
diff --git a/libappfuse/include/libappfuse/FuseBridgeLoop.h b/libappfuse/include/libappfuse/FuseBridgeLoop.h
index 2006532..38043bc 100644
--- a/libappfuse/include/libappfuse/FuseBridgeLoop.h
+++ b/libappfuse/include/libappfuse/FuseBridgeLoop.h
@@ -21,7 +21,9 @@
namespace android {
-class FuseBridgeLoop {
+// TODO: Remove the class after switching to StartFuseBridgeLoop in the
+// framework code.
+class FuseBridgeLoop final {
public:
class Callback {
public:
@@ -30,11 +32,15 @@
};
bool Start(int dev_fd, int proxy_fd, Callback* callback);
-
- private:
- FuseBuffer buffer_;
};
+namespace fuse {
+
+class FuseBridgeLoopCallback : public FuseBridgeLoop::Callback {};
+bool StartFuseBridgeLoop(
+ int dev_fd, int proxy_fd, FuseBridgeLoopCallback* callback);
+
+} // namespace fuse
} // namespace android
#endif // ANDROID_LIBAPPFUSE_FUSEBRIDGELOOP_H_
diff --git a/libappfuse/include/libappfuse/FuseBuffer.h b/libappfuse/include/libappfuse/FuseBuffer.h
index 071b777..e7f620c 100644
--- a/libappfuse/include/libappfuse/FuseBuffer.h
+++ b/libappfuse/include/libappfuse/FuseBuffer.h
@@ -20,6 +20,7 @@
#include <linux/fuse.h>
namespace android {
+namespace fuse {
// The numbers came from sdcard.c.
// Maximum number of bytes to write/read in one request/one reply.
@@ -27,9 +28,9 @@
constexpr size_t kFuseMaxRead = 128 * 1024;
constexpr int32_t kFuseSuccess = 0;
-template<typename T, typename Header>
-struct FuseMessage {
- Header header;
+template<typename T>
+class FuseMessage {
+ public:
bool Read(int fd);
bool Write(int fd) const;
private:
@@ -37,33 +38,53 @@
bool CheckResult(int result, const char* operation_name) const;
};
-struct FuseRequest : public FuseMessage<FuseRequest, fuse_in_header> {
+// FuseRequest represents file operation requests from /dev/fuse. It starts
+// from fuse_in_header. The body layout depends on the operation code.
+struct FuseRequest : public FuseMessage<FuseRequest> {
+ fuse_in_header header;
union {
+ // for FUSE_WRITE
struct {
fuse_write_in write_in;
char write_data[kFuseMaxWrite];
};
+ // for FUSE_OPEN
fuse_open_in open_in;
+ // for FUSE_INIT
fuse_init_in init_in;
+ // for FUSE_READ
fuse_read_in read_in;
+ // for FUSE_LOOKUP
char lookup_name[0];
};
+ void Reset(uint32_t data_length, uint32_t opcode, uint64_t unique);
};
-struct FuseResponse : public FuseMessage<FuseResponse, fuse_out_header> {
+// FuseResponse represents file operation responses to /dev/fuse. It starts
+// from fuse_out_header. The body layout depends on the operation code.
+struct FuseResponse : public FuseMessage<FuseResponse> {
+ fuse_out_header header;
union {
+ // for FUSE_INIT
fuse_init_out init_out;
+ // for FUSE_LOOKUP
fuse_entry_out entry_out;
+ // for FUSE_GETATTR
fuse_attr_out attr_out;
+ // for FUSE_OPEN
fuse_open_out open_out;
+ // for FUSE_READ
char read_data[kFuseMaxRead];
+ // for FUSE_WRITE
fuse_write_out write_out;
};
void Reset(uint32_t data_length, int32_t error, uint64_t unique);
void ResetHeader(uint32_t data_length, int32_t error, uint64_t unique);
};
-union FuseBuffer {
+// To reduce memory usage, FuseBuffer shares the memory region for request and
+// response.
+union FuseBuffer final {
FuseRequest request;
FuseResponse response;
@@ -71,19 +92,7 @@
void HandleNotImpl();
};
-class FuseProxyLoop {
- class IFuseProxyLoopCallback {
- public:
- virtual void OnMount() = 0;
- virtual ~IFuseProxyLoopCallback() = default;
- };
-
- bool Start(int dev_fd, int proxy_fd, IFuseProxyLoopCallback* callback);
-
- private:
- FuseBuffer buffer_;
-};
-
+} // namespace fuse
} // namespace android
#endif // ANDROID_LIBAPPFUSE_FUSEBUFFER_H_
diff --git a/libappfuse/tests/FuseAppLoopTest.cc b/libappfuse/tests/FuseAppLoopTest.cc
new file mode 100644
index 0000000..25906cf
--- /dev/null
+++ b/libappfuse/tests/FuseAppLoopTest.cc
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2016 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 specic language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libappfuse/FuseAppLoop.h"
+
+#include <sys/socket.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <thread>
+
+namespace android {
+namespace fuse {
+namespace {
+
+constexpr unsigned int kTestFileSize = 1024;
+
+struct CallbackRequest {
+ uint32_t code;
+ uint64_t inode;
+};
+
+class Callback : public FuseAppLoopCallback {
+ public:
+ std::vector<CallbackRequest> requests;
+
+ bool IsActive() override {
+ return true;
+ }
+
+ int64_t OnGetSize(uint64_t inode) override {
+ if (inode == FUSE_ROOT_ID) {
+ return 0;
+ } else {
+ return kTestFileSize;
+ }
+ }
+
+ int32_t OnFsync(uint64_t inode) override {
+ requests.push_back({
+ .code = FUSE_FSYNC,
+ .inode = inode
+ });
+ return 0;
+ }
+
+ int32_t OnWrite(uint64_t inode,
+ uint64_t offset ATTRIBUTE_UNUSED,
+ uint32_t size ATTRIBUTE_UNUSED,
+ const void* data ATTRIBUTE_UNUSED) override {
+ requests.push_back({
+ .code = FUSE_WRITE,
+ .inode = inode
+ });
+ return 0;
+ }
+
+ int32_t OnRead(uint64_t inode,
+ uint64_t offset ATTRIBUTE_UNUSED,
+ uint32_t size ATTRIBUTE_UNUSED,
+ void* data ATTRIBUTE_UNUSED) override {
+ requests.push_back({
+ .code = FUSE_READ,
+ .inode = inode
+ });
+ return 0;
+ }
+
+ int32_t OnOpen(uint64_t inode) override {
+ requests.push_back({
+ .code = FUSE_OPEN,
+ .inode = inode
+ });
+ return 0;
+ }
+
+ int32_t OnRelease(uint64_t inode) override {
+ requests.push_back({
+ .code = FUSE_RELEASE,
+ .inode = inode
+ });
+ return 0;
+ }
+};
+
+class FuseAppLoopTest : public ::testing::Test {
+ private:
+ std::thread thread_;
+
+ protected:
+ base::unique_fd sockets_[2];
+ Callback callback_;
+ FuseRequest request_;
+ FuseResponse response_;
+
+ void SetUp() override {
+ base::SetMinimumLogSeverity(base::VERBOSE);
+ int sockets[2];
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets));
+ sockets_[0].reset(sockets[0]);
+ sockets_[1].reset(sockets[1]);
+ thread_ = std::thread([this] {
+ StartFuseAppLoop(sockets_[1].release(), &callback_);
+ });
+ }
+
+ void CheckCallback(
+ size_t data_size, uint32_t code, size_t expected_out_size) {
+ request_.Reset(data_size, code, 1);
+ request_.header.nodeid = 10;
+
+ ASSERT_TRUE(request_.Write(sockets_[0]));
+ ASSERT_TRUE(response_.Read(sockets_[0]));
+
+ Close();
+
+ EXPECT_EQ(kFuseSuccess, response_.header.error);
+ EXPECT_EQ(sizeof(fuse_out_header) + expected_out_size,
+ response_.header.len);
+ EXPECT_EQ(1u, response_.header.unique);
+
+ ASSERT_EQ(1u, callback_.requests.size());
+ EXPECT_EQ(code, callback_.requests[0].code);
+ EXPECT_EQ(10u, callback_.requests[0].inode);
+ }
+
+ void Close() {
+ sockets_[0].reset();
+ sockets_[1].reset();
+ if (thread_.joinable()) {
+ thread_.join();
+ }
+ }
+
+ void TearDown() override {
+ Close();
+ }
+};
+
+} // namespace
+
+TEST_F(FuseAppLoopTest, LookUp) {
+ request_.Reset(3u, FUSE_LOOKUP, 1);
+ request_.header.nodeid = FUSE_ROOT_ID;
+ strcpy(request_.lookup_name, "10");
+
+ ASSERT_TRUE(request_.Write(sockets_[0].get()));
+ ASSERT_TRUE(response_.Read(sockets_[0].get()));
+
+ EXPECT_EQ(kFuseSuccess, response_.header.error);
+ EXPECT_EQ(sizeof(fuse_out_header) + sizeof(fuse_entry_out),
+ response_.header.len);
+ EXPECT_EQ(1u, response_.header.unique);
+
+ EXPECT_EQ(10u, response_.entry_out.nodeid);
+ EXPECT_EQ(0u, response_.entry_out.generation);
+ EXPECT_EQ(10u, response_.entry_out.entry_valid);
+ EXPECT_EQ(10u, response_.entry_out.attr_valid);
+ EXPECT_EQ(0u, response_.entry_out.entry_valid_nsec);
+ EXPECT_EQ(0u, response_.entry_out.attr_valid_nsec);
+
+ EXPECT_EQ(10u, response_.entry_out.attr.ino);
+ EXPECT_EQ(kTestFileSize, response_.entry_out.attr.size);
+ EXPECT_EQ(0u, response_.entry_out.attr.blocks);
+ EXPECT_EQ(0u, response_.entry_out.attr.atime);
+ EXPECT_EQ(0u, response_.entry_out.attr.mtime);
+ EXPECT_EQ(0u, response_.entry_out.attr.ctime);
+ EXPECT_EQ(0u, response_.entry_out.attr.atimensec);
+ EXPECT_EQ(0u, response_.entry_out.attr.mtimensec);
+ EXPECT_EQ(0u, response_.entry_out.attr.ctimensec);
+ EXPECT_EQ(S_IFREG | 0777u, response_.entry_out.attr.mode);
+ EXPECT_EQ(0u, response_.entry_out.attr.nlink);
+ EXPECT_EQ(0u, response_.entry_out.attr.uid);
+ EXPECT_EQ(0u, response_.entry_out.attr.gid);
+ EXPECT_EQ(0u, response_.entry_out.attr.rdev);
+ EXPECT_EQ(0u, response_.entry_out.attr.blksize);
+ EXPECT_EQ(0u, response_.entry_out.attr.padding);
+}
+
+TEST_F(FuseAppLoopTest, LookUp_InvalidName) {
+ request_.Reset(3u, FUSE_LOOKUP, 1);
+ request_.header.nodeid = FUSE_ROOT_ID;
+ strcpy(request_.lookup_name, "aa");
+
+ ASSERT_TRUE(request_.Write(sockets_[0].get()));
+ ASSERT_TRUE(response_.Read(sockets_[0].get()));
+
+ EXPECT_EQ(sizeof(fuse_out_header), response_.header.len);
+ EXPECT_EQ(-ENOENT, response_.header.error);
+ EXPECT_EQ(1u, response_.header.unique);
+}
+
+TEST_F(FuseAppLoopTest, LookUp_TooLargeName) {
+ request_.Reset(21u, FUSE_LOOKUP, 1);
+ request_.header.nodeid = FUSE_ROOT_ID;
+ strcpy(request_.lookup_name, "18446744073709551616");
+
+ ASSERT_TRUE(request_.Write(sockets_[0].get()));
+ ASSERT_TRUE(response_.Read(sockets_[0].get()));
+
+ EXPECT_EQ(sizeof(fuse_out_header), response_.header.len);
+ EXPECT_EQ(-ENOENT, response_.header.error);
+ EXPECT_EQ(1u, response_.header.unique);
+}
+
+TEST_F(FuseAppLoopTest, GetAttr) {
+ request_.Reset(sizeof(fuse_getattr_in), FUSE_GETATTR, 1);
+ request_.header.nodeid = 10;
+
+ ASSERT_TRUE(request_.Write(sockets_[0].get()));
+ ASSERT_TRUE(response_.Read(sockets_[0].get()));
+
+ EXPECT_EQ(kFuseSuccess, response_.header.error);
+ EXPECT_EQ(sizeof(fuse_out_header) + sizeof(fuse_attr_out),
+ response_.header.len);
+ EXPECT_EQ(1u, response_.header.unique);
+
+ EXPECT_EQ(10u, response_.attr_out.attr_valid);
+ EXPECT_EQ(0u, response_.attr_out.attr_valid_nsec);
+
+ EXPECT_EQ(10u, response_.attr_out.attr.ino);
+ EXPECT_EQ(kTestFileSize, response_.attr_out.attr.size);
+ EXPECT_EQ(0u, response_.attr_out.attr.blocks);
+ EXPECT_EQ(0u, response_.attr_out.attr.atime);
+ EXPECT_EQ(0u, response_.attr_out.attr.mtime);
+ EXPECT_EQ(0u, response_.attr_out.attr.ctime);
+ EXPECT_EQ(0u, response_.attr_out.attr.atimensec);
+ EXPECT_EQ(0u, response_.attr_out.attr.mtimensec);
+ EXPECT_EQ(0u, response_.attr_out.attr.ctimensec);
+ EXPECT_EQ(S_IFREG | 0777u, response_.attr_out.attr.mode);
+ EXPECT_EQ(0u, response_.attr_out.attr.nlink);
+ EXPECT_EQ(0u, response_.attr_out.attr.uid);
+ EXPECT_EQ(0u, response_.attr_out.attr.gid);
+ EXPECT_EQ(0u, response_.attr_out.attr.rdev);
+ EXPECT_EQ(0u, response_.attr_out.attr.blksize);
+ EXPECT_EQ(0u, response_.attr_out.attr.padding);
+}
+
+TEST_F(FuseAppLoopTest, GetAttr_Root) {
+ request_.Reset(sizeof(fuse_getattr_in), FUSE_GETATTR, 1);
+ request_.header.nodeid = FUSE_ROOT_ID;
+
+ ASSERT_TRUE(request_.Write(sockets_[0].get()));
+ ASSERT_TRUE(response_.Read(sockets_[0].get()));
+
+ EXPECT_EQ(kFuseSuccess, response_.header.error);
+ EXPECT_EQ(sizeof(fuse_out_header) + sizeof(fuse_attr_out),
+ response_.header.len);
+ EXPECT_EQ(1u, response_.header.unique);
+
+ EXPECT_EQ(10u, response_.attr_out.attr_valid);
+ EXPECT_EQ(0u, response_.attr_out.attr_valid_nsec);
+
+ EXPECT_EQ(static_cast<unsigned>(FUSE_ROOT_ID), response_.attr_out.attr.ino);
+ EXPECT_EQ(0u, response_.attr_out.attr.size);
+ EXPECT_EQ(0u, response_.attr_out.attr.blocks);
+ EXPECT_EQ(0u, response_.attr_out.attr.atime);
+ EXPECT_EQ(0u, response_.attr_out.attr.mtime);
+ EXPECT_EQ(0u, response_.attr_out.attr.ctime);
+ EXPECT_EQ(0u, response_.attr_out.attr.atimensec);
+ EXPECT_EQ(0u, response_.attr_out.attr.mtimensec);
+ EXPECT_EQ(0u, response_.attr_out.attr.ctimensec);
+ EXPECT_EQ(S_IFDIR | 0777u, response_.attr_out.attr.mode);
+ EXPECT_EQ(0u, response_.attr_out.attr.nlink);
+ EXPECT_EQ(0u, response_.attr_out.attr.uid);
+ EXPECT_EQ(0u, response_.attr_out.attr.gid);
+ EXPECT_EQ(0u, response_.attr_out.attr.rdev);
+ EXPECT_EQ(0u, response_.attr_out.attr.blksize);
+ EXPECT_EQ(0u, response_.attr_out.attr.padding);
+}
+
+TEST_F(FuseAppLoopTest, Open) {
+ CheckCallback(sizeof(fuse_open_in), FUSE_OPEN, sizeof(fuse_open_out));
+}
+
+TEST_F(FuseAppLoopTest, Fsync) {
+ CheckCallback(0u, FUSE_FSYNC, 0u);
+}
+
+TEST_F(FuseAppLoopTest, Release) {
+ CheckCallback(0u, FUSE_RELEASE, 0u);
+}
+
+TEST_F(FuseAppLoopTest, Read) {
+ CheckCallback(sizeof(fuse_read_in), FUSE_READ, 0u);
+}
+
+TEST_F(FuseAppLoopTest, Write) {
+ CheckCallback(sizeof(fuse_write_in), FUSE_WRITE, sizeof(fuse_write_out));
+}
+
+} // namespace fuse
+} // namespace android
diff --git a/libappfuse/tests/FuseBridgeLoopTest.cc b/libappfuse/tests/FuseBridgeLoopTest.cc
index 31e3690..bd503eb 100644
--- a/libappfuse/tests/FuseBridgeLoopTest.cc
+++ b/libappfuse/tests/FuseBridgeLoopTest.cc
@@ -21,11 +21,15 @@
#include <sstream>
#include <thread>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
namespace android {
+namespace fuse {
+namespace {
-class Callback : public FuseBridgeLoop::Callback {
+class Callback : public FuseBridgeLoopCallback {
public:
bool mounted;
Callback() : mounted(false) {}
@@ -36,20 +40,28 @@
class FuseBridgeLoopTest : public ::testing::Test {
protected:
- int dev_sockets_[2];
- int proxy_sockets_[2];
+ base::unique_fd dev_sockets_[2];
+ base::unique_fd proxy_sockets_[2];
Callback callback_;
std::thread thread_;
FuseRequest request_;
FuseResponse response_;
- void SetUp() {
- ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, dev_sockets_));
- ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, proxy_sockets_));
+ void SetUp() override {
+ base::SetMinimumLogSeverity(base::VERBOSE);
+ int dev_sockets[2];
+ int proxy_sockets[2];
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, dev_sockets));
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, proxy_sockets));
+ dev_sockets_[0].reset(dev_sockets[0]);
+ dev_sockets_[1].reset(dev_sockets[1]);
+ proxy_sockets_[0].reset(proxy_sockets[0]);
+ proxy_sockets_[1].reset(proxy_sockets[1]);
+
thread_ = std::thread([this] {
- FuseBridgeLoop loop;
- loop.Start(dev_sockets_[1], proxy_sockets_[0], &callback_);
+ StartFuseBridgeLoop(
+ dev_sockets_[1].release(), proxy_sockets_[0].release(), &callback_);
});
}
@@ -103,20 +115,22 @@
}
void Close() {
- close(dev_sockets_[0]);
- close(dev_sockets_[1]);
- close(proxy_sockets_[0]);
- close(proxy_sockets_[1]);
+ dev_sockets_[0].reset();
+ dev_sockets_[1].reset();
+ proxy_sockets_[0].reset();
+ proxy_sockets_[1].reset();
if (thread_.joinable()) {
thread_.join();
}
}
- void TearDown() {
+ void TearDown() override {
Close();
}
};
+} // namespace
+
TEST_F(FuseBridgeLoopTest, FuseInit) {
SendInitRequest(1u);
@@ -156,11 +170,11 @@
CheckNotImpl(FUSE_RENAME);
CheckNotImpl(FUSE_LINK);
CheckNotImpl(FUSE_STATFS);
- CheckNotImpl(FUSE_FSYNC);
CheckNotImpl(FUSE_SETXATTR);
CheckNotImpl(FUSE_GETXATTR);
CheckNotImpl(FUSE_LISTXATTR);
CheckNotImpl(FUSE_REMOVEXATTR);
+ CheckNotImpl(FUSE_FLUSH);
CheckNotImpl(FUSE_OPENDIR);
CheckNotImpl(FUSE_READDIR);
CheckNotImpl(FUSE_RELEASEDIR);
@@ -190,7 +204,8 @@
CheckProxy(FUSE_READ);
CheckProxy(FUSE_WRITE);
CheckProxy(FUSE_RELEASE);
- CheckProxy(FUSE_FLUSH);
+ CheckProxy(FUSE_FSYNC);
}
-} // android
+} // namespace fuse
+} // namespace android
diff --git a/libappfuse/tests/FuseBufferTest.cc b/libappfuse/tests/FuseBufferTest.cc
index 1aacfe3..17f1306 100644
--- a/libappfuse/tests/FuseBufferTest.cc
+++ b/libappfuse/tests/FuseBufferTest.cc
@@ -24,6 +24,7 @@
#include <gtest/gtest.h>
namespace android {
+namespace fuse {
constexpr char kTempFile[] = "/data/local/tmp/appfuse_test_dump";
@@ -183,5 +184,6 @@
ASSERT_EQ(sizeof(fuse_out_header), buffer.response.header.len);
EXPECT_EQ(-ENOSYS, buffer.response.header.error);
}
-}
- // namespace android
+
+} // namespace fuse
+} // namespace android
diff --git a/libprocinfo/.clang-format b/libprocinfo/.clang-format
new file mode 100644
index 0000000..b8c6428
--- /dev/null
+++ b/libprocinfo/.clang-format
@@ -0,0 +1,14 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 2
+PointerAlignment: Left
+TabWidth: 2
+UseTab: Never
+PenaltyExcessCharacter: 32
+
+Cpp11BracedListStyle: false
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
new file mode 100644
index 0000000..8e17f1b
--- /dev/null
+++ b/libprocinfo/Android.bp
@@ -0,0 +1,73 @@
+//
+// 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.
+//
+
+libprocinfo_cppflags = [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+]
+
+cc_library {
+ name: "libprocinfo",
+ host_supported: true,
+ srcs: [
+ "process.cpp",
+ ],
+ cppflags: libprocinfo_cppflags,
+
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+ shared_libs: ["libbase"],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ windows: {
+ enabled: false,
+ },
+ },
+}
+
+// Tests
+// ------------------------------------------------------------------------------
+cc_test {
+ name: "libprocinfo_test",
+ host_supported: true,
+ srcs: [
+ "process_test.cpp",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ windows: {
+ enabled: false,
+ },
+ },
+
+ cppflags: libprocinfo_cppflags,
+ shared_libs: ["libbase", "libprocinfo"],
+
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+}
diff --git a/libprocinfo/include/procinfo/process.h b/libprocinfo/include/procinfo/process.h
new file mode 100644
index 0000000..fb140ff
--- /dev/null
+++ b/libprocinfo/include/procinfo/process.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <type_traits>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace procinfo {
+
+#if defined(__linux__)
+
+struct ProcessInfo {
+ std::string name;
+ pid_t tid;
+ pid_t pid;
+ pid_t ppid;
+ pid_t tracer;
+ uid_t uid;
+ uid_t gid;
+};
+
+// Parse the contents of /proc/<tid>/status into |process_info|.
+bool GetProcessInfo(pid_t tid, ProcessInfo* process_info);
+
+// Parse the contents of <fd>/status into |process_info|.
+// |fd| should be an fd pointing at a /proc/<pid> directory.
+bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info);
+
+// Fetch the list of threads from a given process's /proc/<pid> directory.
+// |fd| should be an fd pointing at a /proc/<pid> directory.
+template <typename Collection>
+auto GetProcessTidsFromProcPidFd(int fd, Collection* out) ->
+ typename std::enable_if<sizeof(typename Collection::value_type) >= sizeof(pid_t), bool>::type {
+ out->clear();
+
+ int task_fd = openat(fd, "task", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(fdopendir(task_fd), closedir);
+ if (!dir) {
+ PLOG(ERROR) << "failed to open task directory";
+ return false;
+ }
+
+ struct dirent* dent;
+ while ((dent = readdir(dir.get()))) {
+ if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0) {
+ pid_t tid;
+ if (!android::base::ParseInt(dent->d_name, &tid, 1, std::numeric_limits<pid_t>::max())) {
+ LOG(ERROR) << "failed to parse task id: " << dent->d_name;
+ return false;
+ }
+
+ out->insert(out->end(), tid);
+ }
+ }
+
+ return true;
+}
+
+template <typename Collection>
+auto GetProcessTids(pid_t pid, Collection* out) ->
+ typename std::enable_if<sizeof(typename Collection::value_type) >= sizeof(pid_t), bool>::type {
+ char task_path[PATH_MAX];
+ if (snprintf(task_path, PATH_MAX, "/proc/%d", pid) >= PATH_MAX) {
+ LOG(ERROR) << "task path overflow (pid = " << pid << ")";
+ return false;
+ }
+
+ android::base::unique_fd fd(open(task_path, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
+ if (fd == -1) {
+ PLOG(ERROR) << "failed to open " << task_path;
+ return false;
+ }
+
+ return GetProcessTidsFromProcPidFd(fd.get(), out);
+}
+
+#endif
+
+} /* namespace procinfo */
+} /* namespace android */
diff --git a/libprocinfo/process.cpp b/libprocinfo/process.cpp
new file mode 100644
index 0000000..c513e16
--- /dev/null
+++ b/libprocinfo/process.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 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 <procinfo/process.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+
+namespace android {
+namespace procinfo {
+
+bool GetProcessInfo(pid_t tid, ProcessInfo* process_info) {
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path), "/proc/%d", tid);
+
+ unique_fd dirfd(open(path, O_DIRECTORY | O_RDONLY));
+ if (dirfd == -1) {
+ PLOG(ERROR) << "failed to open " << path;
+ return false;
+ }
+
+ return GetProcessInfoFromProcPidFd(dirfd.get(), process_info);
+}
+
+bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info) {
+ int status_fd = openat(fd, "status", O_RDONLY | O_CLOEXEC);
+
+ if (status_fd == -1) {
+ PLOG(ERROR) << "failed to open status fd in GetProcessInfoFromProcPidFd";
+ return false;
+ }
+
+ std::unique_ptr<FILE, decltype(&fclose)> fp(fdopen(status_fd, "r"), fclose);
+ if (!fp) {
+ PLOG(ERROR) << "failed to open status file in GetProcessInfoFromProcPidFd";
+ close(status_fd);
+ return false;
+ }
+
+ int field_bitmap = 0;
+ static constexpr int finished_bitmap = 127;
+ char* line = nullptr;
+ size_t len = 0;
+
+ while (getline(&line, &len, fp.get()) != -1 && field_bitmap != finished_bitmap) {
+ char* tab = strchr(line, '\t');
+ if (tab == nullptr) {
+ continue;
+ }
+
+ size_t header_len = tab - line;
+ std::string header = std::string(line, header_len);
+ if (header == "Name:") {
+ std::string name = line + header_len + 1;
+
+ // line includes the trailing newline.
+ name.pop_back();
+ process_info->name = std::move(name);
+
+ field_bitmap |= 1;
+ } else if (header == "Pid:") {
+ process_info->tid = atoi(tab + 1);
+ field_bitmap |= 2;
+ } else if (header == "Tgid:") {
+ process_info->pid = atoi(tab + 1);
+ field_bitmap |= 4;
+ } else if (header == "PPid:") {
+ process_info->ppid = atoi(tab + 1);
+ field_bitmap |= 8;
+ } else if (header == "TracerPid:") {
+ process_info->tracer = atoi(tab + 1);
+ field_bitmap |= 16;
+ } else if (header == "Uid:") {
+ process_info->uid = atoi(tab + 1);
+ field_bitmap |= 32;
+ } else if (header == "Gid:") {
+ process_info->gid = atoi(tab + 1);
+ field_bitmap |= 64;
+ }
+ }
+
+ free(line);
+ return field_bitmap == finished_bitmap;
+}
+
+} /* namespace procinfo */
+} /* namespace android */
diff --git a/libprocinfo/process_test.cpp b/libprocinfo/process_test.cpp
new file mode 100644
index 0000000..5ffd236
--- /dev/null
+++ b/libprocinfo/process_test.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 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 <procinfo/process.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <set>
+#include <thread>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/stringprintf.h>
+
+#if !defined(__BIONIC__)
+#include <syscall.h>
+static pid_t gettid() {
+ return syscall(__NR_gettid);
+}
+#endif
+
+TEST(process_info, process_info_smoke) {
+ android::procinfo::ProcessInfo self;
+ ASSERT_TRUE(android::procinfo::GetProcessInfo(gettid(), &self));
+ ASSERT_EQ(gettid(), self.tid);
+ ASSERT_EQ(getpid(), self.pid);
+ ASSERT_EQ(getppid(), self.ppid);
+ ASSERT_EQ(getuid(), self.uid);
+ ASSERT_EQ(getgid(), self.gid);
+}
+
+TEST(process_info, process_info_proc_pid_fd_smoke) {
+ android::procinfo::ProcessInfo self;
+ int fd = open(android::base::StringPrintf("/proc/%d", gettid()).c_str(), O_DIRECTORY | O_RDONLY);
+ ASSERT_NE(-1, fd);
+ ASSERT_TRUE(android::procinfo::GetProcessInfoFromProcPidFd(fd, &self));
+
+ // Process name is capped at 15 bytes.
+ ASSERT_EQ("libprocinfo_tes", self.name);
+ ASSERT_EQ(gettid(), self.tid);
+ ASSERT_EQ(getpid(), self.pid);
+ ASSERT_EQ(getppid(), self.ppid);
+ ASSERT_EQ(getuid(), self.uid);
+ ASSERT_EQ(getgid(), self.gid);
+ close(fd);
+}
+
+TEST(process_info, process_tids_smoke) {
+ pid_t main_tid = gettid();
+ std::thread([main_tid]() {
+ pid_t thread_tid = gettid();
+
+ {
+ std::vector<pid_t> vec;
+ ASSERT_TRUE(android::procinfo::GetProcessTids(getpid(), &vec));
+ ASSERT_EQ(1, std::count(vec.begin(), vec.end(), main_tid));
+ ASSERT_EQ(1, std::count(vec.begin(), vec.end(), thread_tid));
+ }
+
+ {
+ std::set<pid_t> set;
+ ASSERT_TRUE(android::procinfo::GetProcessTids(getpid(), &set));
+ ASSERT_EQ(1, std::count(set.begin(), set.end(), main_tid));
+ ASSERT_EQ(1, std::count(set.begin(), set.end(), thread_tid));
+ }
+ }).join();
+}