summaryrefslogtreecommitdiffstats
path: root/Utilities/cmlibuv/src/unix/fs.c
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/cmlibuv/src/unix/fs.c')
-rw-r--r--Utilities/cmlibuv/src/unix/fs.c299
1 files changed, 165 insertions, 134 deletions
diff --git a/Utilities/cmlibuv/src/unix/fs.c b/Utilities/cmlibuv/src/unix/fs.c
index a6cc6db..bffc956 100644
--- a/Utilities/cmlibuv/src/unix/fs.c
+++ b/Utilities/cmlibuv/src/unix/fs.c
@@ -43,7 +43,6 @@
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
-#include <utime.h>
#include <poll.h>
#if defined(__DragonFly__) || \
@@ -62,11 +61,20 @@
#if defined(__APPLE__)
# include <copyfile.h>
+# include <sys/sysctl.h>
#elif defined(__linux__) && !defined(FICLONE)
# include <sys/ioctl.h>
# define FICLONE _IOW(0x94, 9, int)
#endif
+#if defined(_AIX) && !defined(_AIX71)
+# include <utime.h>
+#endif
+
+#if defined(_AIX) && _XOPEN_SOURCE <= 600
+extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */
+#endif
+
#define INIT(subtype) \
do { \
if (req == NULL) \
@@ -120,7 +128,11 @@
do { \
if (cb != NULL) { \
uv__req_register(loop, req); \
- uv__work_submit(loop, &req->work_req, uv__fs_work, uv__fs_done); \
+ uv__work_submit(loop, \
+ &req->work_req, \
+ UV__WORK_FAST_IO, \
+ uv__fs_work, \
+ uv__fs_done); \
return 0; \
} \
else { \
@@ -143,7 +155,7 @@ static ssize_t uv__fs_fsync(uv_fs_t* req) {
int r;
r = fcntl(req->file, F_FULLFSYNC);
- if (r != 0 && errno == ENOTTY)
+ if (r != 0)
r = fsync(req->file);
return r;
#else
@@ -165,59 +177,17 @@ static ssize_t uv__fs_fdatasync(uv_fs_t* req) {
static ssize_t uv__fs_futime(uv_fs_t* req) {
-#if defined(__linux__)
+#if defined(__linux__) \
+ || defined(_AIX71)
/* utimesat() has nanosecond resolution but we stick to microseconds
* for the sake of consistency with other platforms.
*/
- static int no_utimesat;
struct timespec ts[2];
- struct timeval tv[2];
- char path[sizeof("/proc/self/fd/") + 3 * sizeof(int)];
- int r;
-
- if (no_utimesat)
- goto skip;
-
ts[0].tv_sec = req->atime;
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;
-
- r = uv__utimesat(req->file, NULL, ts, 0);
- if (r == 0)
- return r;
-
- if (errno != ENOSYS)
- return r;
-
- no_utimesat = 1;
-
-skip:
-
- tv[0].tv_sec = req->atime;
- tv[0].tv_usec = (uint64_t)(req->atime * 1000000) % 1000000;
- tv[1].tv_sec = req->mtime;
- tv[1].tv_usec = (uint64_t)(req->mtime * 1000000) % 1000000;
- snprintf(path, sizeof(path), "/proc/self/fd/%d", (int) req->file);
-
- r = utimes(path, tv);
- if (r == 0)
- return r;
-
- switch (errno) {
- case ENOENT:
- if (fcntl(req->file, F_GETFL) == -1 && errno == EBADF)
- break;
- /* Fall through. */
-
- case EACCES:
- case ENOTDIR:
- errno = ENOSYS;
- break;
- }
-
- return r;
-
+ return futimens(req->file, ts);
#elif defined(__APPLE__) \
|| defined(__DragonFly__) \
|| defined(__FreeBSD__) \
@@ -235,13 +205,6 @@ skip:
# else
return futimes(req->file, tv);
# endif
-#elif defined(_AIX71)
- struct timespec ts[2];
- ts[0].tv_sec = req->atime;
- 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;
- return futimens(req->file, ts);
#elif defined(__MVS__)
attrib_t atr;
memset(&atr, 0, sizeof(atr));
@@ -314,17 +277,13 @@ static ssize_t uv__fs_read(uv_fs_t* req) {
#if defined(__linux__)
static int no_preadv;
#endif
+ unsigned int iovmax;
ssize_t result;
-#if defined(_AIX)
- struct stat buf;
- if(fstat(req->file, &buf))
- return -1;
- if(S_ISDIR(buf.st_mode)) {
- errno = EISDIR;
- return -1;
- }
-#endif /* defined(_AIX) */
+ iovmax = uv__getiovmax();
+ if (req->nbufs > iovmax)
+ req->nbufs = iovmax;
+
if (req->off < 0) {
if (req->nbufs == 1)
result = read(req->file, req->bufs[0].base, req->bufs[0].len);
@@ -343,25 +302,7 @@ static ssize_t uv__fs_read(uv_fs_t* req) {
if (no_preadv) retry:
# endif
{
- off_t nread;
- size_t index;
-
- nread = 0;
- index = 0;
- result = 1;
- do {
- if (req->bufs[index].len > 0) {
- result = pread(req->file,
- req->bufs[index].base,
- req->bufs[index].len,
- req->off + nread);
- if (result > 0)
- nread += result;
- }
- index++;
- } while (index < req->nbufs && result > 0);
- if (nread > 0)
- result = nread;
+ result = pread(req->file, req->bufs[0].base, req->bufs[0].len, req->off);
}
# if defined(__linux__)
else {
@@ -379,6 +320,13 @@ static ssize_t uv__fs_read(uv_fs_t* req) {
}
done:
+ /* Early cleanup of bufs allocation, since we're done with it. */
+ if (req->bufs != req->bufsml)
+ uv__free(req->bufs);
+
+ req->bufs = NULL;
+ req->nbufs = 0;
+
return result;
}
@@ -446,8 +394,10 @@ static ssize_t uv__fs_pathmax_size(const char* path) {
}
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)
/* We may not have a real PATH_MAX. Read size of link. */
@@ -461,17 +411,17 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) {
return -1;
}
- len = st.st_size;
+ maxlen = st.st_size;
/* According to readlink(2) lstat can report st_size == 0
for some symlinks, such as those in /proc or /sys. */
- if (len == 0)
- len = uv__fs_pathmax_size(req->path);
+ if (maxlen == 0)
+ maxlen = uv__fs_pathmax_size(req->path);
#else
- len = uv__fs_pathmax_size(req->path);
+ maxlen = uv__fs_pathmax_size(req->path);
#endif
- buf = uv__malloc(len + 1);
+ buf = uv__malloc(maxlen);
if (buf == NULL) {
errno = ENOMEM;
@@ -479,17 +429,28 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) {
}
#if defined(__MVS__)
- len = os390_readlink(req->path, buf, len);
+ len = os390_readlink(req->path, buf, maxlen);
#else
- len = readlink(req->path, buf, len);
+ len = readlink(req->path, buf, maxlen);
#endif
-
if (len == -1) {
uv__free(buf);
return -1;
}
+ /* Uncommon case: resize to make room for the trailing nul byte. */
+ if (len == maxlen) {
+ newbuf = uv__realloc(buf, len + 1);
+
+ if (newbuf == NULL) {
+ uv__free(buf);
+ return -1;
+ }
+
+ buf = newbuf;
+ }
+
buf[len] = '\0';
req->ptr = buf;
@@ -739,10 +700,48 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) {
static ssize_t uv__fs_utime(uv_fs_t* req) {
+#if defined(__linux__) \
+ || defined(_AIX71) \
+ || defined(__sun)
+ /* utimesat() has nanosecond resolution but we stick to microseconds
+ * for the sake of consistency with other platforms.
+ */
+ struct timespec ts[2];
+ ts[0].tv_sec = req->atime;
+ 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;
+ return utimensat(AT_FDCWD, req->path, ts, 0);
+#elif defined(__APPLE__) \
+ || defined(__DragonFly__) \
+ || defined(__FreeBSD__) \
+ || defined(__FreeBSD_kernel__) \
+ || defined(__NetBSD__) \
+ || defined(__OpenBSD__)
+ struct timeval tv[2];
+ tv[0].tv_sec = req->atime;
+ tv[0].tv_usec = (uint64_t)(req->atime * 1000000) % 1000000;
+ tv[1].tv_sec = req->mtime;
+ tv[1].tv_usec = (uint64_t)(req->mtime * 1000000) % 1000000;
+ return utimes(req->path, tv);
+#elif defined(_AIX) \
+ && !defined(_AIX71)
struct utimbuf buf;
buf.actime = req->atime;
buf.modtime = req->mtime;
- return utime(req->path, &buf); /* TODO use utimes() where available */
+ return utime(req->path, &buf);
+#elif defined(__MVS__)
+ attrib_t atr;
+ memset(&atr, 0, sizeof(atr));
+ atr.att_mtimechg = 1;
+ atr.att_atimechg = 1;
+ atr.att_mtime = req->mtime;
+ atr.att_atime = req->atime;
+ return __lchattr((char*) req->path, &atr, sizeof(atr));
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
}
@@ -780,25 +779,7 @@ static ssize_t uv__fs_write(uv_fs_t* req) {
if (no_pwritev) retry:
# endif
{
- off_t written;
- size_t index;
-
- written = 0;
- index = 0;
- r = 0;
- do {
- if (req->bufs[index].len > 0) {
- r = pwrite(req->file,
- req->bufs[index].base,
- req->bufs[index].len,
- req->off + written);
- if (r > 0)
- written += r;
- }
- index++;
- } while (index < req->nbufs && r >= 0);
- if (written > 0)
- r = written;
+ r = pwrite(req->file, req->bufs[0].base, req->bufs[0].len, req->off);
}
# if defined(__linux__)
else {
@@ -827,26 +808,41 @@ done:
static ssize_t uv__fs_copyfile(uv_fs_t* req) {
#if defined(__APPLE__) && !TARGET_OS_IPHONE
/* On macOS, use the native copyfile(3). */
+ static int can_clone;
copyfile_flags_t flags;
+ char buf[64];
+ size_t len;
+ int major;
flags = COPYFILE_ALL;
if (req->flags & UV_FS_COPYFILE_EXCL)
flags |= COPYFILE_EXCL;
-#ifdef COPYFILE_CLONE
- if (req->flags & UV_FS_COPYFILE_FICLONE)
- flags |= COPYFILE_CLONE;
-#endif
-
+ /* Check OS version. Cloning is only supported on macOS >= 10.12. */
if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) {
-#ifdef COPYFILE_CLONE_FORCE
- flags |= COPYFILE_CLONE_FORCE;
-#else
- return UV_ENOSYS;
-#endif
+ if (can_clone == 0) {
+ len = sizeof(buf);
+ if (sysctlbyname("kern.osrelease", buf, &len, NULL, 0))
+ return UV__ERR(errno);
+
+ if (1 != sscanf(buf, "%d", &major))
+ abort();
+
+ can_clone = -1 + 2 * (major >= 16); /* macOS >= 10.12 */
+ }
+
+ if (can_clone < 0)
+ return UV_ENOSYS;
}
+ /* copyfile() simply ignores COPYFILE_CLONE if it's not supported. */
+ if (req->flags & UV_FS_COPYFILE_FICLONE)
+ flags |= 1 << 24; /* COPYFILE_CLONE */
+
+ if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE)
+ flags |= 1 << 25; /* COPYFILE_CLONE_FORCE */
+
return copyfile(req->path, req->new_path, NULL, flags);
#else
uv_fs_t fs_req;
@@ -906,9 +902,11 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) {
/* 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) ||
- req->flags & UV_FS_COPYFILE_FICLONE_FORCE) {
- err = -errno;
+ 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 {
@@ -968,7 +966,11 @@ out:
}
}
- return result;
+ if (result == 0)
+ return 0;
+
+ errno = UV__ERR(result);
+ return -1;
#endif
}
@@ -1084,9 +1086,21 @@ static int uv__fs_fstat(int fd, uv_stat_t *buf) {
return ret;
}
+static size_t uv__fs_buf_offset(uv_buf_t* bufs, size_t size) {
+ size_t offset;
+ /* Figure out which bufs are done */
+ for (offset = 0; size > 0 && bufs[offset].len <= size; ++offset)
+ size -= bufs[offset].len;
+
+ /* Fix a partial read/write */
+ if (size > 0) {
+ bufs[offset].base += size;
+ bufs[offset].len -= size;
+ }
+ return offset;
+}
-typedef ssize_t (*uv__fs_buf_iter_processor)(uv_fs_t* req);
-static ssize_t uv__fs_buf_iter(uv_fs_t* req, uv__fs_buf_iter_processor process) {
+static ssize_t uv__fs_write_all(uv_fs_t* req) {
unsigned int iovmax;
unsigned int nbufs;
uv_buf_t* bufs;
@@ -1103,7 +1117,10 @@ static ssize_t uv__fs_buf_iter(uv_fs_t* req, uv__fs_buf_iter_processor process)
if (req->nbufs > iovmax)
req->nbufs = iovmax;
- result = process(req);
+ do
+ result = uv__fs_write(req);
+ while (result < 0 && errno == EINTR);
+
if (result <= 0) {
if (total == 0)
total = result;
@@ -1113,14 +1130,12 @@ static ssize_t uv__fs_buf_iter(uv_fs_t* req, uv__fs_buf_iter_processor process)
if (req->off >= 0)
req->off += result;
+ req->nbufs = uv__fs_buf_offset(req->bufs, result);
req->bufs += req->nbufs;
nbufs -= req->nbufs;
total += result;
}
- if (errno == EINTR && total == -1)
- return total;
-
if (bufs != req->bufsml)
uv__free(bufs);
@@ -1137,7 +1152,8 @@ static void uv__fs_work(struct uv__work* w) {
ssize_t r;
req = container_of(w, uv_fs_t, work_req);
- retry_on_eintr = !(req->fs_type == UV_FS_CLOSE);
+ retry_on_eintr = !(req->fs_type == UV_FS_CLOSE ||
+ req->fs_type == UV_FS_READ);
do {
errno = 0;
@@ -1155,6 +1171,7 @@ static void uv__fs_work(struct uv__work* w) {
X(COPYFILE, uv__fs_copyfile(req));
X(FCHMOD, fchmod(req->file, req->mode));
X(FCHOWN, fchown(req->file, req->uid, req->gid));
+ X(LCHOWN, lchown(req->path, req->uid, req->gid));
X(FDATASYNC, uv__fs_fdatasync(req));
X(FSTAT, uv__fs_fstat(req->file, &req->statbuf));
X(FSYNC, uv__fs_fsync(req));
@@ -1165,7 +1182,7 @@ static void uv__fs_work(struct uv__work* w) {
X(MKDIR, mkdir(req->path, req->mode));
X(MKDTEMP, uv__fs_mkdtemp(req));
X(OPEN, uv__fs_open(req));
- X(READ, uv__fs_buf_iter(req, uv__fs_read));
+ X(READ, uv__fs_read(req));
X(SCANDIR, uv__fs_scandir(req));
X(READLINK, uv__fs_readlink(req));
X(REALPATH, uv__fs_realpath(req));
@@ -1176,7 +1193,7 @@ static void uv__fs_work(struct uv__work* w) {
X(SYMLINK, symlink(req->path, req->new_path));
X(UNLINK, unlink(req->path));
X(UTIME, uv__fs_utime(req));
- X(WRITE, uv__fs_buf_iter(req, uv__fs_write));
+ X(WRITE, uv__fs_write_all(req));
default: abort();
}
#undef X
@@ -1281,6 +1298,20 @@ int uv_fs_fchown(uv_loop_t* loop,
}
+int uv_fs_lchown(uv_loop_t* loop,
+ uv_fs_t* req,
+ const char* path,
+ uv_uid_t uid,
+ uv_gid_t gid,
+ uv_fs_cb cb) {
+ INIT(LCHOWN);
+ PATH;
+ req->uid = uid;
+ req->gid = gid;
+ POST;
+}
+
+
int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) {
INIT(FDATASYNC);
req->file = file;