summaryrefslogtreecommitdiffstats
path: root/Utilities/cmlibuv/src/unix/fs.c
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2020-04-06 15:58:14 (GMT)
committerBrad King <brad.king@kitware.com>2020-04-06 15:58:14 (GMT)
commit722d6b41053c6f143e10468d2ea25146a3dcf7a5 (patch)
tree1cd58dcc2f2a255d6cc388f45d4883b0757d262a /Utilities/cmlibuv/src/unix/fs.c
parent21c5a311c60da34d4e66a1d1a778106110f9b5d2 (diff)
parent394b07af40e1bdbdca0033e53ca803585454da18 (diff)
downloadCMake-722d6b41053c6f143e10468d2ea25146a3dcf7a5.zip
CMake-722d6b41053c6f143e10468d2ea25146a3dcf7a5.tar.gz
CMake-722d6b41053c6f143e10468d2ea25146a3dcf7a5.tar.bz2
Merge branch 'upstream-libuv' into update-libuv
* upstream-libuv: libuv 2020-04-06 (d21f5aea)
Diffstat (limited to 'Utilities/cmlibuv/src/unix/fs.c')
-rw-r--r--Utilities/cmlibuv/src/unix/fs.c341
1 files changed, 286 insertions, 55 deletions
diff --git a/Utilities/cmlibuv/src/unix/fs.c b/Utilities/cmlibuv/src/unix/fs.c
index 5fb34f1b..70fe21a 100644
--- a/Utilities/cmlibuv/src/unix/fs.c
+++ b/Utilities/cmlibuv/src/unix/fs.c
@@ -30,6 +30,7 @@
#include "internal.h"
#include <errno.h>
+#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -70,6 +71,20 @@
# include <utime.h>
#endif
+#if defined(__APPLE__) || \
+ defined(__DragonFly__) || \
+ defined(__FreeBSD__) || \
+ defined(__FreeBSD_kernel__) || \
+ defined(__OpenBSD__) || \
+ defined(__NetBSD__)
+# include <sys/param.h>
+# include <sys/mount.h>
+#elif defined(__sun) || defined(__MVS__) || defined(__NetBSD__) || defined(__HAIKU__)
+# include <sys/statvfs.h>
+#else
+# include <sys/statfs.h>
+#endif
+
#if defined(_AIX) && _XOPEN_SOURCE <= 600
extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */
#endif
@@ -202,7 +217,11 @@ static ssize_t uv__fs_futime(uv_fs_t* req) {
ts[0].tv_nsec = (uint64_t)(req->atime * 1000000) % 1000000 * 1000;
ts[1].tv_sec = req->mtime;
ts[1].tv_nsec = (uint64_t)(req->mtime * 1000000) % 1000000 * 1000;
+#if defined(__ANDROID_API__) && __ANDROID_API__ < 21
+ return utimensat(req->file, NULL, ts, 0);
+#else
return futimens(req->file, ts);
+#endif
#elif defined(__APPLE__) \
|| defined(__DragonFly__) \
|| defined(__FreeBSD__) \
@@ -250,23 +269,99 @@ static ssize_t uv__fs_mkdtemp(uv_fs_t* req) {
}
-static ssize_t uv__fs_open(uv_fs_t* req) {
- static int no_cloexec_support;
+static int (*uv__mkostemp)(char*, int);
+
+
+static void uv__mkostemp_initonce(void) {
+ /* z/os doesn't have RTLD_DEFAULT but that's okay
+ * because it doesn't have mkostemp(O_CLOEXEC) either.
+ */
+#ifdef RTLD_DEFAULT
+ uv__mkostemp = (int (*)(char*, int)) dlsym(RTLD_DEFAULT, "mkostemp");
+
+ /* We don't care about errors, but we do want to clean them up.
+ * If there has been no error, then dlerror() will just return
+ * NULL.
+ */
+ dlerror();
+#endif /* RTLD_DEFAULT */
+}
+
+
+static int uv__fs_mkstemp(uv_fs_t* req) {
+ static uv_once_t once = UV_ONCE_INIT;
int r;
+#ifdef O_CLOEXEC
+ static int no_cloexec_support;
+#endif
+ static const char pattern[] = "XXXXXX";
+ static const size_t pattern_size = sizeof(pattern) - 1;
+ char* path;
+ size_t path_length;
+
+ path = (char*) req->path;
+ path_length = strlen(path);
+
+ /* EINVAL can be returned for 2 reasons:
+ 1. The template's last 6 characters were not XXXXXX
+ 2. open() didn't support O_CLOEXEC
+ We want to avoid going to the fallback path in case
+ of 1, so it's manually checked before. */
+ if (path_length < pattern_size ||
+ strcmp(path + path_length - pattern_size, pattern)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ uv_once(&once, uv__mkostemp_initonce);
- /* Try O_CLOEXEC before entering locks */
- if (no_cloexec_support == 0) {
#ifdef O_CLOEXEC
- r = open(req->path, req->flags | O_CLOEXEC, req->mode);
+ if (no_cloexec_support == 0 && uv__mkostemp != NULL) {
+ r = uv__mkostemp(path, O_CLOEXEC);
+
if (r >= 0)
return r;
+
+ /* If mkostemp() returns EINVAL, it means the kernel doesn't
+ support O_CLOEXEC, so we just fallback to mkstemp() below. */
if (errno != EINVAL)
return r;
+
+ /* We set the static variable so that next calls don't even
+ try to use mkostemp. */
no_cloexec_support = 1;
+ }
#endif /* O_CLOEXEC */
+
+ if (req->cb != NULL)
+ uv_rwlock_rdlock(&req->loop->cloexec_lock);
+
+ r = mkstemp(path);
+
+ /* In case of failure `uv__cloexec` will leave error in `errno`,
+ * so it is enough to just set `r` to `-1`.
+ */
+ if (r >= 0 && uv__cloexec(r, 1) != 0) {
+ r = uv__close(r);
+ if (r != 0)
+ abort();
+ r = -1;
}
if (req->cb != NULL)
+ uv_rwlock_rdunlock(&req->loop->cloexec_lock);
+
+ return r;
+}
+
+
+static ssize_t uv__fs_open(uv_fs_t* req) {
+#ifdef O_CLOEXEC
+ return open(req->path, req->flags | O_CLOEXEC, req->mode);
+#else /* O_CLOEXEC */
+ int r;
+
+ if (req->cb != NULL)
uv_rwlock_rdlock(&req->loop->cloexec_lock);
r = open(req->path, req->flags, req->mode);
@@ -285,9 +380,60 @@ static ssize_t uv__fs_open(uv_fs_t* req) {
uv_rwlock_rdunlock(&req->loop->cloexec_lock);
return r;
+#endif /* O_CLOEXEC */
}
+#if !HAVE_PREADV
+static ssize_t uv__fs_preadv(uv_file fd,
+ uv_buf_t* bufs,
+ unsigned int nbufs,
+ off_t off) {
+ uv_buf_t* buf;
+ uv_buf_t* end;
+ ssize_t result;
+ ssize_t rc;
+ size_t pos;
+
+ assert(nbufs > 0);
+
+ result = 0;
+ pos = 0;
+ buf = bufs + 0;
+ end = bufs + nbufs;
+
+ for (;;) {
+ do
+ rc = pread(fd, buf->base + pos, buf->len - pos, off + result);
+ while (rc == -1 && errno == EINTR);
+
+ if (rc == 0)
+ break;
+
+ if (rc == -1 && result == 0)
+ return UV__ERR(errno);
+
+ if (rc == -1)
+ break; /* We read some data so return that, ignore the error. */
+
+ pos += rc;
+ result += rc;
+
+ if (pos < buf->len)
+ continue;
+
+ pos = 0;
+ buf += 1;
+
+ if (buf == end)
+ break;
+ }
+
+ return result;
+}
+#endif
+
+
static ssize_t uv__fs_read(uv_fs_t* req) {
#if defined(__linux__)
static int no_preadv;
@@ -317,7 +463,7 @@ static ssize_t uv__fs_read(uv_fs_t* req) {
if (no_preadv) retry:
# endif
{
- result = pread(req->file, req->bufs[0].base, req->bufs[0].len, req->off);
+ result = uv__fs_preadv(req->file, req->bufs, req->nbufs, req->off);
}
# if defined(__linux__)
else {
@@ -481,14 +627,39 @@ static int uv__fs_closedir(uv_fs_t* req) {
return 0;
}
-#if defined(_POSIX_PATH_MAX)
-# define UV__FS_PATH_MAX _POSIX_PATH_MAX
-#elif defined(PATH_MAX)
-# define UV__FS_PATH_MAX PATH_MAX
+static int uv__fs_statfs(uv_fs_t* req) {
+ uv_statfs_t* stat_fs;
+#if defined(__sun) || defined(__MVS__) || defined(__NetBSD__) || defined(__HAIKU__)
+ struct statvfs buf;
+
+ if (0 != statvfs(req->path, &buf))
+#else
+ struct statfs buf;
+
+ if (0 != statfs(req->path, &buf))
+#endif /* defined(__sun) */
+ return -1;
+
+ stat_fs = uv__malloc(sizeof(*stat_fs));
+ if (stat_fs == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+#if defined(__sun) || defined(__MVS__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__)
+ stat_fs->f_type = 0; /* f_type is not supported. */
#else
-# define UV__FS_PATH_MAX_FALLBACK 8192
-# define UV__FS_PATH_MAX UV__FS_PATH_MAX_FALLBACK
+ stat_fs->f_type = buf.f_type;
#endif
+ stat_fs->f_bsize = buf.f_bsize;
+ stat_fs->f_blocks = buf.f_blocks;
+ stat_fs->f_bfree = buf.f_bfree;
+ stat_fs->f_bavail = buf.f_bavail;
+ stat_fs->f_files = buf.f_files;
+ stat_fs->f_ffree = buf.f_ffree;
+ req->ptr = stat_fs;
+ return 0;
+}
static ssize_t uv__fs_pathmax_size(const char* path) {
ssize_t pathmax;
@@ -496,7 +667,7 @@ static ssize_t uv__fs_pathmax_size(const char* path) {
pathmax = pathconf(path, _PC_PATH_MAX);
if (pathmax == -1)
- pathmax = UV__FS_PATH_MAX;
+ pathmax = UV__PATH_MAX;
return pathmax;
}
@@ -505,9 +676,10 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) {
ssize_t maxlen;
ssize_t len;
char* buf;
- char* newbuf;
-#if defined(UV__FS_PATH_MAX_FALLBACK)
+#if defined(_POSIX_PATH_MAX) || defined(PATH_MAX)
+ maxlen = uv__fs_pathmax_size(req->path);
+#else
/* We may not have a real PATH_MAX. Read size of link. */
struct stat st;
int ret;
@@ -525,8 +697,6 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) {
for some symlinks, such as those in /proc or /sys. */
if (maxlen == 0)
maxlen = uv__fs_pathmax_size(req->path);
-#else
- maxlen = uv__fs_pathmax_size(req->path);
#endif
buf = uv__malloc(maxlen);
@@ -549,14 +719,10 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) {
/* Uncommon case: resize to make room for the trailing nul byte. */
if (len == maxlen) {
- newbuf = uv__realloc(buf, len + 1);
+ buf = uv__reallocf(buf, len + 1);
- if (newbuf == NULL) {
- uv__free(buf);
+ if (buf == NULL)
return -1;
- }
-
- buf = newbuf;
}
buf[len] = '\0';
@@ -918,12 +1084,14 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) {
uv_fs_t fs_req;
uv_file srcfd;
uv_file dstfd;
- struct stat statsbuf;
+ struct stat src_statsbuf;
+ struct stat dst_statsbuf;
int dst_flags;
int result;
int err;
size_t bytes_to_send;
int64_t in_offset;
+ ssize_t bytes_written;
dstfd = -1;
err = 0;
@@ -936,7 +1104,7 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) {
return srcfd;
/* Get the source file's mode. */
- if (fstat(srcfd, &statsbuf)) {
+ if (fstat(srcfd, &src_statsbuf)) {
err = UV__ERR(errno);
goto out;
}
@@ -951,7 +1119,7 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) {
&fs_req,
req->new_path,
dst_flags,
- statsbuf.st_mode,
+ src_statsbuf.st_mode,
NULL);
uv_fs_req_cleanup(&fs_req);
@@ -960,26 +1128,55 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) {
goto out;
}
- if (fchmod(dstfd, statsbuf.st_mode) == -1) {
+ /* Get the destination file's mode. */
+ if (fstat(dstfd, &dst_statsbuf)) {
err = UV__ERR(errno);
goto out;
}
+ /* Check if srcfd and dstfd refer to the same file */
+ if (src_statsbuf.st_dev == dst_statsbuf.st_dev &&
+ src_statsbuf.st_ino == dst_statsbuf.st_ino) {
+ goto out;
+ }
+
+ if (fchmod(dstfd, src_statsbuf.st_mode) == -1) {
+ err = UV__ERR(errno);
+#ifdef __linux__
+ if (err != UV_EPERM)
+ goto out;
+
+ {
+ struct statfs s;
+
+ /* fchmod() on CIFS shares always fails with EPERM unless the share is
+ * mounted with "noperm". As fchmod() is a meaningless operation on such
+ * shares anyway, detect that condition and squelch the error.
+ */
+ if (fstatfs(dstfd, &s) == -1)
+ goto out;
+
+ if (s.f_type != /* CIFS */ 0xFF534D42u)
+ goto out;
+ }
+
+ err = 0;
+#else /* !__linux__ */
+ goto out;
+#endif /* !__linux__ */
+ }
+
#ifdef FICLONE
if (req->flags & UV_FS_COPYFILE_FICLONE ||
req->flags & UV_FS_COPYFILE_FICLONE_FORCE) {
- if (ioctl(dstfd, FICLONE, srcfd) == -1) {
- /* If an error occurred that the sendfile fallback also won't handle, or
- this is a force clone then exit. Otherwise, fall through to try using
- sendfile(). */
- if (errno != ENOTTY && errno != EOPNOTSUPP && errno != EXDEV) {
- err = UV__ERR(errno);
- goto out;
- } else if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) {
- err = UV_ENOTSUP;
- goto out;
- }
- } else {
+ if (ioctl(dstfd, FICLONE, srcfd) == 0) {
+ /* ioctl() with FICLONE succeeded. */
+ goto out;
+ }
+ /* If an error occurred and force was set, return the error to the caller;
+ * fall back to sendfile() when force was not set. */
+ if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) {
+ err = UV__ERR(errno);
goto out;
}
}
@@ -990,21 +1187,20 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) {
}
#endif
- bytes_to_send = statsbuf.st_size;
+ bytes_to_send = src_statsbuf.st_size;
in_offset = 0;
while (bytes_to_send != 0) {
- err = uv_fs_sendfile(NULL,
- &fs_req,
- dstfd,
- srcfd,
- in_offset,
- bytes_to_send,
- NULL);
+ uv_fs_sendfile(NULL, &fs_req, dstfd, srcfd, in_offset, bytes_to_send, NULL);
+ bytes_written = fs_req.result;
uv_fs_req_cleanup(&fs_req);
- if (err < 0)
+
+ if (bytes_written < 0) {
+ err = bytes_written;
break;
- bytes_to_send -= fs_req.result;
- in_offset += fs_req.result;
+ }
+
+ bytes_to_send -= bytes_written;
+ in_offset += bytes_written;
}
out:
@@ -1151,13 +1347,22 @@ static int uv__fs_statx(int fd,
rc = uv__statx(dirfd, path, flags, mode, &statxbuf);
- if (rc == -1) {
+ switch (rc) {
+ case 0:
+ break;
+ case -1:
/* EPERM happens when a seccomp filter rejects the system call.
* Has been observed with libseccomp < 2.3.3 and docker < 18.04.
*/
if (errno != EINVAL && errno != EPERM && errno != ENOSYS)
return -1;
-
+ /* Fall through. */
+ default:
+ /* Normally on success, zero is returned and On error, -1 is returned.
+ * Observed on S390 RHEL running in a docker container with statx not
+ * implemented, rc might return 1 with 0 set as the error code in which
+ * case we return ENOSYS.
+ */
no_statx = 1;
return UV_ENOSYS;
}
@@ -1332,6 +1537,7 @@ static void uv__fs_work(struct uv__work* w) {
X(LINK, link(req->path, req->new_path));
X(MKDIR, mkdir(req->path, req->mode));
X(MKDTEMP, uv__fs_mkdtemp(req));
+ X(MKSTEMP, uv__fs_mkstemp(req));
X(OPEN, uv__fs_open(req));
X(READ, uv__fs_read(req));
X(SCANDIR, uv__fs_scandir(req));
@@ -1344,6 +1550,7 @@ static void uv__fs_work(struct uv__work* w) {
X(RMDIR, rmdir(req->path));
X(SENDFILE, uv__fs_sendfile(req));
X(STAT, uv__fs_stat(req->path, &req->statbuf));
+ X(STATFS, uv__fs_statfs(req));
X(SYMLINK, symlink(req->path, req->new_path));
X(UNLINK, unlink(req->path));
X(UTIME, uv__fs_utime(req));
@@ -1555,6 +1762,18 @@ int uv_fs_mkdtemp(uv_loop_t* loop,
}
+int uv_fs_mkstemp(uv_loop_t* loop,
+ uv_fs_t* req,
+ const char* tpl,
+ uv_fs_cb cb) {
+ INIT(MKSTEMP);
+ req->path = uv__strdup(tpl);
+ if (req->path == NULL)
+ return UV_ENOMEM;
+ POST;
+}
+
+
int uv_fs_open(uv_loop_t* loop,
uv_fs_t* req,
const char* path,
@@ -1773,10 +1992,12 @@ void uv_fs_req_cleanup(uv_fs_t* req) {
/* Only necessary for asychronous requests, i.e., requests with a callback.
* Synchronous ones don't copy their arguments and have req->path and
- * req->new_path pointing to user-owned memory. UV_FS_MKDTEMP is the
- * exception to the rule, it always allocates memory.
+ * req->new_path pointing to user-owned memory. UV_FS_MKDTEMP and
+ * UV_FS_MKSTEMP are the exception to the rule, they always allocate memory.
*/
- if (req->path != NULL && (req->cb != NULL || req->fs_type == UV_FS_MKDTEMP))
+ if (req->path != NULL &&
+ (req->cb != NULL ||
+ req->fs_type == UV_FS_MKDTEMP || req->fs_type == UV_FS_MKSTEMP))
uv__free((void*) req->path); /* Memory is shared with req->new_path. */
req->path = NULL;
@@ -1816,3 +2037,13 @@ int uv_fs_copyfile(uv_loop_t* loop,
req->flags = flags;
POST;
}
+
+
+int uv_fs_statfs(uv_loop_t* loop,
+ uv_fs_t* req,
+ const char* path,
+ uv_fs_cb cb) {
+ INIT(STATFS);
+ PATH;
+ POST;
+}