Merge "Add POSIX fexecve."
diff --git a/libc/bionic/exec.cpp b/libc/bionic/exec.cpp
index 2001106..1cf3a58 100644
--- a/libc/bionic/exec.cpp
+++ b/libc/bionic/exec.cpp
@@ -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 @@
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;
+}
diff --git a/libc/bionic/fchmod.cpp b/libc/bionic/fchmod.cpp
index ace8c6b..a486aae 100644
--- a/libc/bionic/fchmod.cpp
+++ b/libc/bionic/fchmod.cpp
@@ -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 @@
// 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.
diff --git a/libc/bionic/fgetxattr.cpp b/libc/bionic/fgetxattr.cpp
index 6d999bf..38b7ac3 100644
--- a/libc/bionic/fgetxattr.cpp
+++ b/libc/bionic/fgetxattr.cpp
@@ -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 @@
// 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);
}
diff --git a/libc/bionic/flistxattr.cpp b/libc/bionic/flistxattr.cpp
index 05a96d2..8ad9b85 100644
--- a/libc/bionic/flistxattr.cpp
+++ b/libc/bionic/flistxattr.cpp
@@ -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 @@
// 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);
}
diff --git a/libc/bionic/fsetxattr.cpp b/libc/bionic/fsetxattr.cpp
index 6d2e868..9ad0c76 100644
--- a/libc/bionic/fsetxattr.cpp
+++ b/libc/bionic/fsetxattr.cpp
@@ -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 @@
// 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);
}
diff --git a/libc/bionic/pty.cpp b/libc/bionic/pty.cpp
index bdabf36..599cbd2 100644
--- a/libc/bionic/pty.cpp
+++ b/libc/bionic/pty.cpp
@@ -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 @@
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;
}
diff --git a/libc/include/unistd.h b/libc/include/unistd.h
index 9cfb918..fe98c10 100644
--- a/libc/include/unistd.h
+++ b/libc/include/unistd.h
@@ -98,6 +98,7 @@
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);
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index 981dd59..af4efb9 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1325,6 +1325,7 @@
endhostent;
endnetent;
endprotoent;
+ fexecve;
getentropy;
getnetent;
getprotoent;
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index 29c5235..5c7f726 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1245,6 +1245,7 @@
endhostent;
endnetent;
endprotoent;
+ fexecve;
getentropy;
getnetent;
getprotoent;
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index eafbbd7..33ecbed 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1350,6 +1350,7 @@
endhostent;
endnetent;
endprotoent;
+ fexecve;
getentropy;
getnetent;
getprotoent;
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index a32131f..579491a 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1309,6 +1309,7 @@
endhostent;
endnetent;
endprotoent;
+ fexecve;
getentropy;
getnetent;
getprotoent;
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index 29c5235..5c7f726 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1245,6 +1245,7 @@
endhostent;
endnetent;
endprotoent;
+ fexecve;
getentropy;
getnetent;
getprotoent;
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index f1308ea..7d1d3ef 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1307,6 +1307,7 @@
endhostent;
endnetent;
endprotoent;
+ fexecve;
getentropy;
getnetent;
getprotoent;
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index 29c5235..5c7f726 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1245,6 +1245,7 @@
endhostent;
endnetent;
endprotoent;
+ fexecve;
getentropy;
getnetent;
getprotoent;
diff --git a/libc/private/FdPath.h b/libc/private/FdPath.h
new file mode 100644
index 0000000..4a6a2d5
--- /dev/null
+++ b/libc/private/FdPath.h
@@ -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];
+};
diff --git a/tests/unistd_test.cpp b/tests/unistd_test.cpp
index 81c6d92..c79ed59 100644
--- a/tests/unistd_test.cpp
+++ b/tests/unistd_test.cpp
@@ -1402,6 +1402,41 @@
"<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));