Add POSIX fexecve.

I'm skeptical about the usefulness of this, but it's in POSIX, it's
in glibc (but not iOS), and it is used in some internal source (test
runners and container code).

Bug: N/A
Test: ran tests
Change-Id: I92c5398f2a679b21a33fba92bc8e67e3ae2eb76f
This commit is contained in:
Elliott Hughes 2017-10-18 15:54:56 -07:00
parent ab9dc08bdd
commit 4d215aad85
16 changed files with 106 additions and 28 deletions

View File

@ -39,6 +39,8 @@
#include <string.h>
#include <unistd.h>
#include "private/FdPath.h"
extern "C" char** environ;
enum ExecVariant { kIsExecL, kIsExecLE, kIsExecLP };
@ -170,3 +172,10 @@ int execvpe(const char* name, char* const* argv, char* const* envp) {
if (saw_EACCES) errno = EACCES;
return -1;
}
int fexecve(int fd, char* const* argv, char* const* envp) {
// execveat with AT_EMPTY_PATH (>= 3.19) seems to offer no advantages.
execve(FdPath(fd).c_str(), argv, envp);
if (errno == ENOENT) errno = EBADF;
return -1;
}

View File

@ -33,13 +33,14 @@
#include <unistd.h>
#include <stdio.h>
#include "private/FdPath.h"
extern "C" int ___fchmod(int, mode_t);
int fchmod(int fd, mode_t mode) {
int saved_errno = errno;
int result = ___fchmod(fd, mode);
if ((result == 0) || (errno != EBADF)) {
if (result == 0 || errno != EBADF) {
return result;
}
@ -52,16 +53,14 @@ int fchmod(int fd, mode_t mode) {
// on an O_PATH file descriptor, and "man open" documents fchmod
// on O_PATH file descriptors as returning EBADF.
int fd_flag = fcntl(fd, F_GETFL);
if ((fd_flag == -1) || ((fd_flag & O_PATH) == 0)) {
if (fd_flag == -1 || (fd_flag & O_PATH) == 0) {
errno = EBADF;
return -1;
}
char buf[40];
snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fd);
errno = saved_errno;
result = chmod(buf, mode);
if ((result == -1) && (errno == ELOOP)) {
result = chmod(FdPath(fd).c_str(), mode);
if (result == -1 && errno == ELOOP) {
// Linux does not support changing the mode of a symlink.
// For fchmodat(AT_SYMLINK_NOFOLLOW), POSIX requires a return
// value of ENOTSUP. Assume that's true here too.

View File

@ -33,13 +33,15 @@
#include <fcntl.h>
#include <stdio.h>
#include "private/FdPath.h"
extern "C" ssize_t ___fgetxattr(int, const char*, void*, size_t);
ssize_t fgetxattr(int fd, const char *name, void *value, size_t size) {
int saved_errno = errno;
ssize_t result = ___fgetxattr(fd, name, value, size);
if ((result != -1) || (errno != EBADF)) {
if (result != -1 || errno != EBADF) {
return result;
}
@ -47,13 +49,11 @@ ssize_t fgetxattr(int fd, const char *name, void *value, size_t size) {
// may not directly support fgetxattr() on such a file descriptor.
// Use /proc/self/fd instead to emulate this support.
int fd_flag = fcntl(fd, F_GETFL);
if ((fd_flag == -1) || ((fd_flag & O_PATH) == 0)) {
if (fd_flag == -1 || (fd_flag & O_PATH) == 0) {
errno = EBADF;
return -1;
}
char buf[40];
snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fd);
errno = saved_errno;
return getxattr(buf, name, value, size);
return getxattr(FdPath(fd).c_str(), name, value, size);
}

View File

@ -33,13 +33,14 @@
#include <fcntl.h>
#include <stdio.h>
#include "private/FdPath.h"
extern "C" ssize_t ___flistxattr(int, char*, size_t);
ssize_t flistxattr(int fd, char *list, size_t size) {
int saved_errno = errno;
ssize_t result = ___flistxattr(fd, list, size);
if ((result != -1) || (errno != EBADF)) {
if (result != -1 || errno != EBADF) {
return result;
}
@ -47,13 +48,11 @@ ssize_t flistxattr(int fd, char *list, size_t size) {
// may not directly support fgetxattr() on such a file descriptor.
// Use /proc/self/fd instead to emulate this support.
int fd_flag = fcntl(fd, F_GETFL);
if ((fd_flag == -1) || ((fd_flag & O_PATH) == 0)) {
if (fd_flag == -1 || (fd_flag & O_PATH) == 0) {
errno = EBADF;
return -1;
}
char buf[40];
snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fd);
errno = saved_errno;
return listxattr(buf, list, size);
return listxattr(FdPath(fd).c_str(), list, size);
}

View File

@ -33,13 +33,14 @@
#include <fcntl.h>
#include <stdio.h>
#include "private/FdPath.h"
extern "C" int ___fsetxattr(int, const char*, const void*, size_t, int);
int fsetxattr(int fd, const char* name, const void* value, size_t size, int flags) {
int saved_errno = errno;
int result = ___fsetxattr(fd, name, value, size, flags);
if ((result == 0) || (errno != EBADF)) {
if (result == 0 || errno != EBADF) {
return result;
}
@ -47,13 +48,11 @@ int fsetxattr(int fd, const char* name, const void* value, size_t size, int flag
// may not directly support fsetxattr() on such a file descriptor.
// Use /proc/self/fd instead to emulate this support.
int fd_flag = fcntl(fd, F_GETFL);
if ((fd_flag == -1) || ((fd_flag & O_PATH) == 0)) {
if (fd_flag == -1 || (fd_flag & O_PATH) == 0) {
errno = EBADF;
return -1;
}
char buf[40];
snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fd);
errno = saved_errno;
return setxattr(buf, name, value, size, flags);
return setxattr(FdPath(fd).c_str(), name, value, size, flags);
}

View File

@ -37,6 +37,7 @@
#include <utmp.h>
#include "bionic/pthread_internal.h"
#include "private/FdPath.h"
int getpt() {
return posix_openpt(O_RDWR|O_NOCTTY);
@ -94,10 +95,7 @@ int ttyname_r(int fd, char* buf, size_t len) {
return errno;
}
char path[64];
snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
ssize_t count = readlink(path, buf, len);
ssize_t count = readlink(FdPath(fd).c_str(), buf, len);
if (count == -1) {
return errno;
}

View File

@ -98,6 +98,7 @@ int execl(const char* __path, const char* __arg0, ...) __attribute__((__sentinel
int execlp(const char* __file, const char* __arg0, ...) __attribute__((__sentinel__));
int execle(const char* __path, const char* __arg0, ... /*, char* const* __envp */)
__attribute__((__sentinel__(1)));
int fexecve(int __fd, char* const* __argv, char* const* __envp) __INTRODUCED_IN_FUTURE;
int nice(int __incr);

View File

@ -1325,6 +1325,7 @@ LIBC_P { # introduced=P
endhostent;
endnetent;
endprotoent;
fexecve;
getentropy;
getnetent;
getprotoent;

View File

@ -1245,6 +1245,7 @@ LIBC_P { # introduced=P
endhostent;
endnetent;
endprotoent;
fexecve;
getentropy;
getnetent;
getprotoent;

View File

@ -1350,6 +1350,7 @@ LIBC_P { # introduced=P
endhostent;
endnetent;
endprotoent;
fexecve;
getentropy;
getnetent;
getprotoent;

View File

@ -1309,6 +1309,7 @@ LIBC_P { # introduced=P
endhostent;
endnetent;
endprotoent;
fexecve;
getentropy;
getnetent;
getprotoent;

View File

@ -1245,6 +1245,7 @@ LIBC_P { # introduced=P
endhostent;
endnetent;
endprotoent;
fexecve;
getentropy;
getnetent;
getprotoent;

View File

@ -1307,6 +1307,7 @@ LIBC_P { # introduced=P
endhostent;
endnetent;
endprotoent;
fexecve;
getentropy;
getnetent;
getprotoent;

View File

@ -1245,6 +1245,7 @@ LIBC_P { # introduced=P
endhostent;
endnetent;
endprotoent;
fexecve;
getentropy;
getnetent;
getprotoent;

31
libc/private/FdPath.h Normal file
View File

@ -0,0 +1,31 @@
/*
* Copyright (C) 2017 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
class FdPath {
public:
explicit FdPath(int fd) {
snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fd);
}
const char* c_str() {
return buf;
}
private:
char buf[40];
};

View File

@ -1376,6 +1376,41 @@ TEST(UNISTD_TEST, exec_argv0_null) {
"<unknown>: usage: run-as");
}
TEST(UNISTD_TEST, fexecve_failure) {
ExecTestHelper eth;
errno = 0;
int fd = open("/", O_RDONLY);
ASSERT_NE(-1, fd);
ASSERT_EQ(-1, fexecve(fd, eth.GetArgs(), eth.GetEnv()));
ASSERT_EQ(EACCES, errno);
close(fd);
}
TEST(UNISTD_TEST, fexecve_bad_fd) {
ExecTestHelper eth;
errno = 0;
ASSERT_EQ(-1, fexecve(-1, eth.GetArgs(), eth.GetEnv()));
ASSERT_EQ(EBADF, errno);
}
TEST(UNISTD_TEST, fexecve_args) {
// Test basic argument passing.
int echo_fd = open(BIN_DIR "echo", O_RDONLY | O_CLOEXEC);
ASSERT_NE(-1, echo_fd);
ExecTestHelper eth;
eth.SetArgs({"echo", "hello", "world", nullptr});
eth.Run([&]() { fexecve(echo_fd, eth.GetArgs(), eth.GetEnv()); }, 0, "hello world\n");
close(echo_fd);
// Test environment variable setting too.
int printenv_fd = open(BIN_DIR "printenv", O_RDONLY | O_CLOEXEC);
ASSERT_NE(-1, printenv_fd);
eth.SetArgs({"printenv", nullptr});
eth.SetEnv({"A=B", nullptr});
eth.Run([&]() { fexecve(printenv_fd, eth.GetArgs(), eth.GetEnv()); }, 0, "A=B\n");
close(printenv_fd);
}
TEST(UNISTD_TEST, getlogin_r) {
char buf[LOGIN_NAME_MAX] = {};
EXPECT_EQ(ERANGE, getlogin_r(buf, 0));