@@ -291,15 +291,16 @@ class Function:
# handle special comments, such as flags=AT_SYMLINK_NOFOLLOW
if self.comments:
+ comments = self.comments.replace('==','<equals>')
# Build a dictionary of key=value, key=value pairs
- modifiers = dict(mod.split("=") for mod in self.comments.split(','))
+ modifiers = dict(mod.split("=") for mod in comments.split(','))
# Strip all leading/trailing whitespace
modifiers = {k.strip():v.strip() for k, v in modifiers.items()}
arch = "-" + platform.machine()
# Sorted so that versions-foo appear after versions, so overrides are easy
for key in sorted(modifiers):
- value = modifiers[key]
+ value = modifiers[key].replace('<equals>', '==')
# If the key is version-arm64 and we're on arm64 then rename this to version
if key.endswith(arch):
key = key.replace(arch, "")
@@ -1,4 +1,4 @@
-int open(const char *path, int flags, ...{mode_t mode}); /* flags=flags&O_NOFOLLOW, noignore_path=1 */
+int open(const char *path, int flags, ...{mode_t mode}); /* flags=((flags&O_NOFOLLOW)||((flags&(O_CREAT|O_EXCL))==(O_CREAT|O_EXCL))), noignore_path=1 */
char *get_current_dir_name(void);
int __xstat(int ver, const char *path, struct stat *buf);
int __lxstat(int ver, const char *path, struct stat *buf); /* flags=AT_SYMLINK_NOFOLLOW */
@@ -6,8 +6,8 @@ int __fxstat(int ver, int fd, struct stat *buf);
int lchmod(const char *path, mode_t mode); /* flags=AT_SYMLINK_NOFOLLOW */
int lchown(const char *path, uid_t owner, gid_t group); /* flags=AT_SYMLINK_NOFOLLOW */
int __fxstatat(int ver, int dirfd, const char *path, struct stat *buf, int flags);
-int openat(int dirfd, const char *path, int flags, ...{mode_t mode}); /* flags=flags&O_NOFOLLOW, noignore_path=1 */
-int __openat_2(int dirfd, const char *path, int flags); /* flags=flags&O_NOFOLLOW, noignore_path=1 */
+int openat(int dirfd, const char *path, int flags, ...{mode_t mode}); /* flags=((flags&O_NOFOLLOW)||((flags&(O_CREAT|O_EXCL))==(O_CREAT|O_EXCL))), noignore_path=1 */
+int __openat_2(int dirfd, const char *path, int flags); /* flags=((flags&O_NOFOLLOW)||((flags&(O_CREAT|O_EXCL))==(O_CREAT|O_EXCL))), noignore_path=1 */
int mknod(const char *path, mode_t mode, dev_t dev); /* real_func=pseudo_mknod */
int mknodat(int dirfd, const char *path, mode_t mode, dev_t dev); /* real_func=pseudo_mknodat */
int __xmknod(int ver, const char *path, mode_t mode, dev_t *dev); /* flags=AT_SYMLINK_NOFOLLOW */
@@ -17,9 +17,9 @@ int fcntl64(int fd, int cmd, ...{struct flock *lock}); /* noignore_path=1 */
# just so we know the inums of symlinks
char *canonicalize_file_name(const char *filename);
int eaccess(const char *path, int mode);
-int open64(const char *path, int flags, ...{mode_t mode}); /* flags=flags&O_NOFOLLOW, noignore_path=1 */
-int openat64(int dirfd, const char *path, int flags, ...{mode_t mode}); /* flags=flags&O_NOFOLLOW, noignore_path=1 */
-int __openat64_2(int dirfd, const char *path, int flags); /* flags=flags&O_NOFOLLOW, noignore_path=1 */
+int open64(const char *path, int flags, ...{mode_t mode}); /* flags=((flags&O_NOFOLLOW)||((flags&(O_CREAT|O_EXCL))==(O_CREAT|O_EXCL))), noignore_path=1 */
+int openat64(int dirfd, const char *path, int flags, ...{mode_t mode}); /* flags=((flags&O_NOFOLLOW)||((flags&(O_CREAT|O_EXCL))==(O_CREAT|O_EXCL))), noignore_path=1 */
+int __openat64_2(int dirfd, const char *path, int flags); /* flags=((flags&O_NOFOLLOW)||((flags&(O_CREAT|O_EXCL))==(O_CREAT|O_EXCL))), noignore_path=1 */
int creat64(const char *path, mode_t mode);
int stat(const char *path, struct stat *buf); /* real_func=pseudo_stat */
int lstat(const char *path, struct stat *buf); /* real_func=pseudo_lstat, flags=AT_SYMLINK_NOFOLLOW */
new file mode 100644
@@ -0,0 +1,99 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+
+struct testcase {
+ const char * pathname;
+ int flags;
+ mode_t mode;
+ int expected_errno;
+};
+
+/*
+* List of test cases to run. The files (given by 'pathname') are expected to be
+* setup by test-open-symlinks.sh.
+*/
+static const struct testcase testcases[] = {
+ { "a", O_WRONLY|O_CREAT|O_EXCL, 0644, EEXIST },
+ { "a", O_RDWR|O_CREAT|O_EXCL, 0644, EEXIST },
+ { "b", O_WRONLY|O_CREAT|O_EXCL, 0644, EEXIST },
+ { "b", O_RDWR|O_CREAT|O_EXCL, 0644, EEXIST },
+ { NULL }
+};
+
+static bool run_testcase(const char * progname,
+ const struct testcase * testcase);
+
+static int try_open(const char * pathname, int flags, mode_t mode);
+
+/*
+* Given a directory (which is expected to be setup containing some files by
+* test-open-symlinks.sh), loop through the test cases listed above and check
+* that when calling open(pathname, flags, mode), errno is set to the expected
+* value.
+*/
+int main(int argc, char *argv[]) {
+ const char * progname = argv[0];
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s <dir>\n", progname);
+ return 1;
+ }
+
+ const char * dirname = argv[1];
+ if (chdir(dirname) != 0) {
+ perror(dirname);
+ return 1;
+ }
+
+ bool ok = true;
+
+ for (int i = 0; testcases[i].pathname; i++) {
+ ok &= run_testcase(progname, &testcases[i]);
+ }
+
+ return ok ? 0 : 1;
+}
+
+/*
+* Given a test case of (pathname, flags, mode, expected_errno), open the file
+* with the given arguments and return true if the actual errno matches the
+* expected one, or otherwise false.
+*/
+static bool run_testcase(const char * progname,
+ const struct testcase * testcase) {
+
+ int actual_errno = try_open(testcase->pathname,
+ testcase->flags,
+ testcase->mode);
+
+ if (actual_errno != testcase->expected_errno) {
+ fprintf(stderr, "%s: open(%s, 0x%x, %o) -> %s (expected: %s)\n",
+ progname, testcase->pathname, testcase->flags, testcase->mode,
+ strerror(actual_errno), strerror(testcase->expected_errno));
+ return false;
+ }
+
+ return true;
+}
+
+/*
+* open() a file with the given flags and mode and return errno (or 0, if the
+* file was successfully opened).
+*/
+static int try_open(const char * pathname, int flags, mode_t mode) {
+ int fd = open(pathname, flags, mode);
+
+ if (fd >= 0) {
+ close(fd);
+ return 0;
+ }
+
+ return errno;
+}
new file mode 100755
@@ -0,0 +1,18 @@
+#! /bin/sh
+
+# * Create a temporary directory
+# * Create some symlinks in the directory
+# * Run test/test-open-symlinks, which will call open(O_CREAT|O_EXCL) on the
+# symlinks in the directory and check that errno is EEXIST.
+
+tmpdir="$(mktemp -d -p "$PWD")"
+trap "rm -rf '$tmpdir'" EXIT TERM INT
+
+# 'a' is a symlink to a file that doesn't exist
+ln -s noexist.txt "$tmpdir/a"
+
+# 'b' is a symlink to a file that doesn't exist and where we also don't have
+# write permissions to the parent directory (of the target).
+ln -s /root/noexist.txt "$tmpdir/b"
+
+./test/test-open-symlinks "$tmpdir"