init: service file keyword am: 62767fe29f am: cc46af0fc9 am: 3b23d2864f
am: 5bee4e3d10
Change-Id: I002ce1c22cc28af947cba609470c628583ed3261
diff --git a/init/Android.mk b/init/Android.mk
index 4bfd743..442a5f3 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -46,6 +46,7 @@
LOCAL_SRC_FILES:= \
action.cpp \
capabilities.cpp \
+ descriptors.cpp \
import_parser.cpp \
init_parser.cpp \
log.cpp \
@@ -125,8 +126,10 @@
LOCAL_SHARED_LIBRARIES += \
libcutils \
libbase \
+ libselinux \
LOCAL_STATIC_LIBRARIES := libinit
LOCAL_SANITIZE := integer
LOCAL_CLANG := true
+LOCAL_CPPFLAGS := -Wall -Wextra -Werror
include $(BUILD_NATIVE_TEST)
diff --git a/init/descriptors.cpp b/init/descriptors.cpp
new file mode 100644
index 0000000..10aae88
--- /dev/null
+++ b/init/descriptors.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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 "descriptors.h"
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/stringprintf.h>
+#include <cutils/files.h>
+#include <cutils/sockets.h>
+
+#include "init.h"
+#include "log.h"
+#include "util.h"
+
+DescriptorInfo::DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
+ gid_t gid, int perm, const std::string& context)
+ : name_(name), type_(type), uid_(uid), gid_(gid), perm_(perm), context_(context) {
+}
+
+DescriptorInfo::~DescriptorInfo() {
+}
+
+std::ostream& operator<<(std::ostream& os, const DescriptorInfo& info) {
+ return os << " descriptors " << info.name_ << " " << info.type_ << " " << std::oct << info.perm_;
+}
+
+bool DescriptorInfo::operator==(const DescriptorInfo& other) const {
+ return name_ == other.name_ && type_ == other.type_ && key() == other.key();
+}
+
+void DescriptorInfo::CreateAndPublish(const std::string& globalContext) const {
+ // Create
+ const std::string& contextStr = context_.empty() ? globalContext : context_;
+ int fd = Create(contextStr);
+ if (fd < 0) return;
+
+ // Publish
+ std::string publishedName = key() + name_;
+ std::for_each(publishedName.begin(), publishedName.end(),
+ [] (char& c) { c = isalnum(c) ? c : '_'; });
+
+ std::string val = android::base::StringPrintf("%d", fd);
+ add_environment(publishedName.c_str(), val.c_str());
+
+ // make sure we don't close on exec
+ fcntl(fd, F_SETFD, 0);
+}
+
+void DescriptorInfo::Clean() const {
+}
+
+SocketInfo::SocketInfo(const std::string& name, const std::string& type, uid_t uid,
+ gid_t gid, int perm, const std::string& context)
+ : DescriptorInfo(name, type, uid, gid, perm, context) {
+}
+
+void SocketInfo::Clean() const {
+ unlink(android::base::StringPrintf(ANDROID_SOCKET_DIR "/%s", name().c_str()).c_str());
+}
+
+int SocketInfo::Create(const std::string& context) const {
+ int flags = ((type() == "stream" ? SOCK_STREAM :
+ (type() == "dgram" ? SOCK_DGRAM :
+ SOCK_SEQPACKET)));
+ return create_socket(name().c_str(), flags, perm(), uid(), gid(), context.c_str());
+}
+
+const std::string SocketInfo::key() const {
+ return ANDROID_SOCKET_ENV_PREFIX;
+}
+
+FileInfo::FileInfo(const std::string& name, const std::string& type, uid_t uid,
+ gid_t gid, int perm, const std::string& context)
+ : DescriptorInfo(name, type, uid, gid, perm, context) {
+}
+
+int FileInfo::Create(const std::string& context) const {
+ int flags = ((type() == "r" ? O_RDONLY :
+ (type() == "w" ? (O_WRONLY | O_CREAT) :
+ (O_RDWR | O_CREAT))));
+ return create_file(name().c_str(), flags, perm(), uid(), gid(), context.c_str());
+}
+
+const std::string FileInfo::key() const {
+ return ANDROID_FILE_ENV_PREFIX;
+}
diff --git a/init/descriptors.h b/init/descriptors.h
new file mode 100644
index 0000000..ff276fb
--- /dev/null
+++ b/init/descriptors.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+
+#ifndef _INIT_DESCRIPTORS_H
+#define _INIT_DESCRIPTORS_H
+
+#include <sys/types.h>
+
+#include <string>
+
+class DescriptorInfo {
+ public:
+ DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
+ gid_t gid, int perm, const std::string& context);
+ virtual ~DescriptorInfo();
+
+ friend std::ostream& operator<<(std::ostream& os, const class DescriptorInfo& info);
+ bool operator==(const DescriptorInfo& other) const;
+
+ void CreateAndPublish(const std::string& globalContext) const;
+ virtual void Clean() const;
+
+ protected:
+ const std::string& name() const { return name_; }
+ const std::string& type() const { return type_; }
+ uid_t uid() const { return uid_; }
+ gid_t gid() const { return gid_; }
+ int perm() const { return perm_; }
+ const std::string& context() const { return context_; }
+
+ private:
+ std::string name_;
+ std::string type_;
+ uid_t uid_;
+ gid_t gid_;
+ int perm_;
+ std::string context_;
+
+ virtual int Create(const std::string& globalContext) const = 0;
+ virtual const std::string key() const = 0;
+};
+
+std::ostream& operator<<(std::ostream& os, const DescriptorInfo& info);
+
+class SocketInfo : public DescriptorInfo {
+ public:
+ SocketInfo(const std::string& name, const std::string& type, uid_t uid,
+ gid_t gid, int perm, const std::string& context);
+ void Clean() const override;
+ private:
+ virtual int Create(const std::string& context) const override;
+ virtual const std::string key() const override;
+};
+
+class FileInfo : public DescriptorInfo {
+ public:
+ FileInfo(const std::string& name, const std::string& type, uid_t uid,
+ gid_t gid, int perm, const std::string& context);
+ private:
+ virtual int Create(const std::string& context) const override;
+ virtual const std::string key() const override;
+};
+
+#endif
diff --git a/init/readme.txt b/init/readme.txt
index e01faa9..5173ca6 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -141,12 +141,20 @@
Set the environment variable <name> to <value> in the launched process.
socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]
- Create a unix domain socket named /dev/socket/<name> and pass
- its fd to the launched process. <type> must be "dgram", "stream" or "seqpacket".
- User and group default to 0.
- 'seclabel' is the SELinux security context for the socket.
- It defaults to the service security context, as specified by seclabel or
- computed based on the service executable file security context.
+ Create a unix domain socket named /dev/socket/<name> and pass its fd to the
+ launched process. <type> must be "dgram", "stream" or "seqpacket". User and
+ group default to 0. 'seclabel' is the SELinux security context for the
+ socket. It defaults to the service security context, as specified by
+ seclabel or computed based on the service executable file security context.
+ For native executables see libcutils android_get_control_socket().
+
+file <path> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]
+ Open/Create a file path and pass its fd to the launched process. <type> must
+ be "r", "w" or "rw". User and group default to 0. 'seclabel' is the SELinux
+ security context for the file if it must be created. It defaults to the
+ service security context, as specified by seclabel or computed based on the
+ service executable file security context. For native executables see
+ libcutils android_get_control_file().
user <username>
Change to 'username' before exec'ing this service.
diff --git a/init/service.cpp b/init/service.cpp
index e052b45..9fa11b8 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -36,7 +36,6 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <cutils/android_reboot.h>
-#include <cutils/sockets.h>
#include <system/thread_defs.h>
#include <processgroup/processgroup.h>
@@ -145,14 +144,6 @@
strs->push_back(nullptr);
}
-SocketInfo::SocketInfo() : uid(0), gid(0), perm(0) {
-}
-
-SocketInfo::SocketInfo(const std::string& name, const std::string& type, uid_t uid,
- gid_t gid, int perm, const std::string& socketcon)
- : name(name), type(type), uid(uid), gid(gid), perm(perm), socketcon(socketcon) {
-}
-
ServiceEnvironmentInfo::ServiceEnvironmentInfo() {
}
@@ -213,20 +204,6 @@
}
}
-void Service::CreateSockets(const std::string& context) {
- for (const auto& si : sockets_) {
- int socket_type = ((si.type == "stream" ? SOCK_STREAM :
- (si.type == "dgram" ? SOCK_DGRAM :
- SOCK_SEQPACKET)));
- const char* socketcon = !si.socketcon.empty() ? si.socketcon.c_str() : context.c_str();
-
- int s = create_socket(si.name.c_str(), socket_type, si.perm, si.uid, si.gid, socketcon);
- if (s >= 0) {
- PublishSocket(si.name, s);
- }
- }
-}
-
void Service::SetProcessAttributes() {
// Keep capabilites on uid change.
if (capabilities_.any() && uid_) {
@@ -273,11 +250,9 @@
KillProcessGroup(SIGKILL);
}
- // Remove any sockets we may have created.
- for (const auto& si : sockets_) {
- std::string tmp = StringPrintf(ANDROID_SOCKET_DIR "/%s", si.name.c_str());
- unlink(tmp.c_str());
- }
+ // Remove any descriptor resources we may have created.
+ std::for_each(descriptors_.begin(), descriptors_.end(),
+ std::bind(&DescriptorInfo::Clean, std::placeholders::_1));
if (flags_ & SVC_EXEC) {
LOG(INFO) << "SVC_EXEC pid " << pid_ << " finished...";
@@ -330,9 +305,8 @@
LOG(INFO) << "service " << name_;
LOG(INFO) << " class '" << classname_ << "'";
LOG(INFO) << " exec "<< android::base::Join(args_, " ");
- for (const auto& si : sockets_) {
- LOG(INFO) << " socket " << si.name << " " << si.type << " " << std::oct << si.perm;
- }
+ std::for_each(descriptors_.begin(), descriptors_.end(),
+ [] (const auto& info) { LOG(INFO) << *info; });
}
bool Service::ParseCapabilities(const std::vector<std::string>& args, std::string* err) {
@@ -469,20 +443,48 @@
return true;
}
-/* name type perm [ uid gid context ] */
+template <typename T>
+bool Service::AddDescriptor(const std::vector<std::string>& args, std::string* err) {
+ int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
+ uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0;
+ gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0;
+ std::string context = args.size() > 6 ? args[6] : "";
+
+ auto descriptor = std::make_unique<T>(args[1], args[2], uid, gid, perm, context);
+
+ auto old =
+ std::find_if(descriptors_.begin(), descriptors_.end(),
+ [&descriptor] (const auto& other) { return descriptor.get() == other.get(); });
+
+ if (old != descriptors_.end()) {
+ *err = "duplicate descriptor " + args[1] + " " + args[2];
+ return false;
+ }
+
+ descriptors_.emplace_back(std::move(descriptor));
+ return true;
+}
+
+// name type perm [ uid gid context ]
bool Service::ParseSocket(const std::vector<std::string>& args, std::string* err) {
if (args[2] != "dgram" && args[2] != "stream" && args[2] != "seqpacket") {
*err = "socket type must be 'dgram', 'stream' or 'seqpacket'";
return false;
}
+ return AddDescriptor<SocketInfo>(args, err);
+}
- int perm = std::strtoul(args[3].c_str(), 0, 8);
- uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0;
- gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0;
- std::string socketcon = args.size() > 6 ? args[6] : "";
-
- sockets_.emplace_back(args[1], args[2], uid, gid, perm, socketcon);
- return true;
+// name type perm [ uid gid context ]
+bool Service::ParseFile(const std::vector<std::string>& args, std::string* err) {
+ if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
+ *err = "file type must be 'r', 'w' or 'rw'";
+ return false;
+ }
+ if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
+ *err = "file name must not be relative";
+ return false;
+ }
+ return AddDescriptor<FileInfo>(args, err);
}
bool Service::ParseUser(const std::vector<std::string>& args, std::string* err) {
@@ -524,6 +526,7 @@
{"seclabel", {1, 1, &Service::ParseSeclabel}},
{"setenv", {2, 2, &Service::ParseSetenv}},
{"socket", {3, 6, &Service::ParseSocket}},
+ {"file", {2, 6, &Service::ParseFile}},
{"user", {1, 1, &Service::ParseUser}},
{"writepid", {1, kMax, &Service::ParseWritepid}},
};
@@ -613,7 +616,8 @@
add_environment(ei.name.c_str(), ei.value.c_str());
}
- CreateSockets(scon);
+ std::for_each(descriptors_.begin(), descriptors_.end(),
+ std::bind(&DescriptorInfo::CreateAndPublish, std::placeholders::_1, scon));
std::string pid_str = StringPrintf("%d", getpid());
for (const auto& file : writepid_files_) {
@@ -787,15 +791,6 @@
close(fd);
}
-void Service::PublishSocket(const std::string& name, int fd) const {
- std::string key = StringPrintf(ANDROID_SOCKET_ENV_PREFIX "%s", name.c_str());
- std::string val = StringPrintf("%d", fd);
- add_environment(key.c_str(), val.c_str());
-
- /* make sure we don't close-on-exec */
- fcntl(fd, F_SETFD, 0);
-}
-
int ServiceManager::exec_count_ = 0;
ServiceManager::ServiceManager() {
diff --git a/init/service.h b/init/service.h
index 7f035ef..d9e8f57 100644
--- a/init/service.h
+++ b/init/service.h
@@ -27,6 +27,7 @@
#include "action.h"
#include "capabilities.h"
+#include "descriptors.h"
#include "init_parser.h"
#include "keyword_map.h"
@@ -48,18 +49,6 @@
class Action;
class ServiceManager;
-struct SocketInfo {
- SocketInfo();
- SocketInfo(const std::string& name, const std::string& type, uid_t uid,
- gid_t gid, int perm, const std::string& socketcon);
- std::string name;
- std::string type;
- uid_t uid;
- gid_t gid;
- int perm;
- std::string socketcon;
-};
-
struct ServiceEnvironmentInfo {
ServiceEnvironmentInfo();
ServiceEnvironmentInfo(const std::string& name, const std::string& value);
@@ -113,9 +102,7 @@
void StopOrReset(int how);
void ZapStdio() const;
void OpenConsole() const;
- void PublishSocket(const std::string& name, int fd) const;
void KillProcessGroup(int signal);
- void CreateSockets(const std::string& scon);
void SetProcessAttributes();
bool ParseCapabilities(const std::vector<std::string>& args, std::string *err);
@@ -134,9 +121,13 @@
bool ParseSeclabel(const std::vector<std::string>& args, std::string* err);
bool ParseSetenv(const std::vector<std::string>& args, std::string* err);
bool ParseSocket(const std::vector<std::string>& args, std::string* err);
+ bool ParseFile(const std::vector<std::string>& args, std::string* err);
bool ParseUser(const std::vector<std::string>& args, std::string* err);
bool ParseWritepid(const std::vector<std::string>& args, std::string* err);
+ template <typename T>
+ bool AddDescriptor(const std::vector<std::string>& args, std::string* err);
+
std::string name_;
std::string classname_;
std::string console_;
@@ -155,7 +146,7 @@
std::string seclabel_;
- std::vector<SocketInfo> sockets_;
+ std::vector<std::unique_ptr<DescriptorInfo>> descriptors_;
std::vector<ServiceEnvironmentInfo> envvars_;
Action onrestart_; // Commands to execute on restart.
diff --git a/init/util.cpp b/init/util.cpp
index 660a66f..b280244 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -14,32 +14,32 @@
* limitations under the License.
*/
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ftw.h>
+#include <pwd.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <errno.h>
#include <time.h>
-#include <ftw.h>
-#include <pwd.h>
+#include <unistd.h>
-#include <selinux/label.h>
#include <selinux/android.h>
+#include <selinux/label.h>
+#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <sys/socket.h>
#include <sys/un.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-
/* for ANDROID_SOCKET_* */
#include <cutils/sockets.h>
-#include <android-base/stringprintf.h>
#include "init.h"
#include "log.h"
@@ -164,6 +164,76 @@
return -1;
}
+/*
+ * create_file - opens and creates a file as dictated in init.rc.
+ * This file is inherited by the daemon. We communicate the file
+ * descriptor's value via the environment variable ANDROID_FILE_<basename>
+ */
+int create_file(const char *path, int flags, mode_t perm, uid_t uid,
+ gid_t gid, const char *filecon)
+{
+ char *secontext = NULL;
+ int ret;
+
+ if (filecon) {
+ if (setsockcreatecon(filecon) == -1) {
+ PLOG(ERROR) << "setsockcreatecon(\"" << filecon << "\") failed";
+ return -1;
+ }
+ } else if (sehandle) {
+ ret = selabel_lookup(sehandle, &secontext, path, perm);
+ if (ret != -1) {
+ ret = setfscreatecon(secontext);
+ if (ret == -1) {
+ freecon(secontext);
+ PLOG(ERROR) << "setfscreatecon(\"" << secontext << "\") failed";
+ return -1;
+ }
+ }
+ }
+
+ int fd = TEMP_FAILURE_RETRY(open(path, flags | O_NDELAY, perm));
+
+ if (filecon) {
+ setsockcreatecon(NULL);
+ lsetfilecon(path, filecon);
+ } else {
+ setfscreatecon(NULL);
+ freecon(secontext);
+ }
+
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open/create file '" << path << "'";
+ goto out_close;
+ }
+
+ if (!(flags & O_NDELAY)) fcntl(fd, F_SETFD, flags);
+
+ ret = lchown(path, uid, gid);
+ if (ret) {
+ PLOG(ERROR) << "Failed to lchown file '" << path << "'";
+ goto out_close;
+ }
+ if (perm != static_cast<mode_t>(-1)) {
+ ret = fchmodat(AT_FDCWD, path, perm, AT_SYMLINK_NOFOLLOW);
+ if (ret) {
+ PLOG(ERROR) << "Failed to fchmodat file '" << path << "'";
+ goto out_close;
+ }
+ }
+
+ LOG(INFO) << "Created file '" << path << "'"
+ << ", mode " << std::oct << perm << std::dec
+ << ", user " << uid
+ << ", group " << gid;
+
+ return fd;
+
+out_close:
+ if (fd >= 0) close(fd);
+ return -1;
+}
+
bool read_file(const char* path, std::string* content) {
content->clear();
diff --git a/init/util.h b/init/util.h
index b83b9a0..b7531cc 100644
--- a/init/util.h
+++ b/init/util.h
@@ -27,6 +27,8 @@
int create_socket(const char *name, int type, mode_t perm,
uid_t uid, gid_t gid, const char *socketcon);
+int create_file(const char *path, int mode, mode_t perm,
+ uid_t uid, gid_t gid, const char *filecon);
bool read_file(const char* path, std::string* content);
int write_file(const char* path, const char* content);
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 228954b..6ecbf90 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -17,7 +17,15 @@
#include "util.h"
#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/files.h>
#include <gtest/gtest.h>
+#include <selinux/android.h>
TEST(util, read_file_ENOENT) {
std::string s("hello");
@@ -41,3 +49,51 @@
EXPECT_EQ(UINT_MAX, decode_uid("toot"));
EXPECT_EQ(123U, decode_uid("123"));
}
+
+struct selabel_handle *sehandle;
+
+TEST(util, create_file) {
+ if (!sehandle) sehandle = selinux_android_file_context_handle();
+
+ static const char path[] = "/data/local/tmp/util.create_file.test";
+ static const char key[] = ANDROID_FILE_ENV_PREFIX "_data_local_tmp_util_create_file_test";
+ EXPECT_EQ(unsetenv(key), 0);
+ unlink(path);
+
+ int fd;
+ uid_t uid = decode_uid("logd");
+ gid_t gid = decode_uid("system");
+ mode_t perms = S_IRWXU | S_IWGRP | S_IRGRP | S_IROTH;
+ static const char context[] = "u:object_r:misc_logd_file:s0";
+ EXPECT_GE(fd = create_file(path, O_RDWR | O_CREAT, perms, uid, gid, context), 0);
+ if (fd < 0) return;
+ static const char hello[] = "hello world\n";
+ static const ssize_t len = strlen(hello);
+ EXPECT_EQ(write(fd, hello, len), len);
+ char buffer[sizeof(hello)];
+ memset(buffer, 0, sizeof(buffer));
+ EXPECT_GE(lseek(fd, 0, SEEK_SET), 0);
+ EXPECT_EQ(read(fd, buffer, sizeof(buffer)), len);
+ EXPECT_EQ(strcmp(hello, buffer), 0);
+ char val[32];
+ snprintf(val, sizeof(val), "%d", fd);
+ EXPECT_EQ(android_get_control_file(path), -1);
+ setenv(key, val, true);
+ EXPECT_EQ(android_get_control_file(path), fd);
+ close(fd);
+ EXPECT_EQ(android_get_control_file(path), -1);
+ EXPECT_EQ(unsetenv(key), 0);
+ struct stat st;
+ EXPECT_EQ(stat(path, &st), 0);
+ EXPECT_EQ(st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO), perms);
+ EXPECT_EQ(st.st_uid, uid);
+ EXPECT_EQ(st.st_gid, gid);
+ security_context_t con;
+ EXPECT_GE(getfilecon(path, &con), 0);
+ EXPECT_NE(con, static_cast<security_context_t>(NULL));
+ if (con) {
+ EXPECT_EQ(context, std::string(con));
+ }
+ freecon(con);
+ EXPECT_EQ(unlink(path), 0);
+}