diff --git a/include/cutils/files.h b/include/cutils/files.h new file mode 100644 index 000000000..0210e3084 --- /dev/null +++ b/include/cutils/files.h @@ -0,0 +1,37 @@ +/* + * 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 __CUTILS_FILES_H +#define __CUTILS_FILES_H + +#define ANDROID_FILE_ENV_PREFIX "ANDROID_FILE_" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * android_get_control_file - simple helper function to get the file + * descriptor of our init-managed file. `path' is the filename path as + * given in init.rc. Returns -1 on error. + */ +int android_get_control_file(const char* path); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_FILES_H */ diff --git a/libcutils/Android.bp b/libcutils/Android.bp index 943926b9b..f7b497d9d 100644 --- a/libcutils/Android.bp +++ b/libcutils/Android.bp @@ -34,6 +34,7 @@ cc_library { host_supported: true, srcs: [ "config_utils.c", + "files.cpp", "fs_config.c", "canned_fs_config.c", "hashmap.c", diff --git a/libcutils/files.cpp b/libcutils/files.cpp new file mode 100644 index 000000000..bf15b4283 --- /dev/null +++ b/libcutils/files.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +// This file contains files implementation that can be shared between +// platforms as long as the correct headers are included. +#define _GNU_SOURCE 1 // for asprintf + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef TEMP_FAILURE_RETRY // _WIN32 does not define +#define TEMP_FAILURE_RETRY(exp) (exp) +#endif + +int android_get_control_file(const char* path) { + if (!path) return -1; + + char *key = NULL; + if (asprintf(&key, ANDROID_FILE_ENV_PREFIX "%s", path) < 0) return -1; + if (!key) return -1; + + char *cp = key; + while (*cp) { + if (!isalnum(*cp)) *cp = '_'; + ++cp; + } + + const char* val = getenv(key); + free(key); + if (!val) return -1; + + errno = 0; + long fd = strtol(val, NULL, 10); + if (errno) return -1; + + // validity checking + if ((fd < 0) || (fd > INT_MAX)) return -1; +#if defined(_SC_OPEN_MAX) + if (fd >= sysconf(_SC_OPEN_MAX)) return -1; +#elif defined(OPEN_MAX) + if (fd >= OPEN_MAX) return -1; +#elif defined(_POSIX_OPEN_MAX) + if (fd >= _POSIX_OPEN_MAX) return -1; +#endif + +#if defined(F_GETFD) + if (TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD)) < 0) return -1; +#elif defined(F_GETFL) + if (TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL)) < 0) return -1; +#else + struct stat s; + if (TEMP_FAILURE_RETRY(fstat(fd, &s)) < 0) return -1; +#endif + +#if defined(__linux__) + char *proc = NULL; + if (asprintf(&proc, "/proc/self/fd/%ld", fd) < 0) return -1; + if (!proc) return -1; + + size_t len = strlen(path); + char *buf = static_cast(calloc(1, len + 2)); + if (!buf) { + free(proc); + return -1; + } + ssize_t ret = TEMP_FAILURE_RETRY(readlink(proc, buf, len + 1)); + free(proc); + int cmp = (len != static_cast(ret)) || strcmp(buf, path); + free(buf); + if (ret < 0) return -1; + if (cmp != 0) return -1; +#endif + + // It is what we think it is + return static_cast(fd); +} diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp index 06d0e284e..bd354129d 100644 --- a/libcutils/tests/Android.bp +++ b/libcutils/tests/Android.bp @@ -14,7 +14,7 @@ cc_defaults { name: "libcutils_test_default", - srcs: ["sockets_test.cpp"], + srcs: ["sockets_test.cpp", "files_test.cpp"], target: { android: { diff --git a/libcutils/tests/files_test.cpp b/libcutils/tests/files_test.cpp new file mode 100644 index 000000000..1a7d67386 --- /dev/null +++ b/libcutils/tests/files_test.cpp @@ -0,0 +1,46 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include + +TEST(FilesTest, android_get_control_file) { + static const char key[] = ANDROID_FILE_ENV_PREFIX "_dev_kmsg"; + static const char name[] = "/dev/kmsg"; + + EXPECT_EQ(unsetenv(key), 0); + EXPECT_EQ(android_get_control_file(name), -1); + + int fd; + ASSERT_GE(fd = open(name, O_RDONLY | O_CLOEXEC), 0); + EXPECT_EQ(android_get_control_file(name), -1); + + char val[32]; + snprintf(val, sizeof(val), "%d", fd); + EXPECT_EQ(setenv(key, val, true), 0); + + EXPECT_EQ(android_get_control_file(name), fd); + close(fd); + EXPECT_EQ(android_get_control_file(name), -1); + EXPECT_EQ(unsetenv(key), 0); + EXPECT_EQ(android_get_control_file(name), -1); +}