X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fshared%2Futil.c;h=1994c7e05d0923036bb51de3796986dd78597f79;hb=012d7b4217420163db5752a63da6cab39d25edf3;hp=a13819e79ac8acb5e21d71a9474412cee2b222a8;hpb=a6dcc7e5924f9c27d3e9c6560a448b02ec28b65f;p=elogind.git diff --git a/src/shared/util.c b/src/shared/util.c index a13819e79..1994c7e05 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -94,6 +94,9 @@ #include "def.h" #include "sparse-endian.h" +/* Put this test here for a lack of better place */ +assert_cc(EAGAIN == EWOULDBLOCK); + int saved_argc = 0; char **saved_argv = NULL; @@ -2592,7 +2595,7 @@ char* dirname_malloc(const char *path) { int dev_urandom(void *p, size_t n) { static int have_syscall = -1; - _cleanup_close_ fd = -1; + _cleanup_close_ int fd = -1; int r; /* Gathers some randomness from the kernel. This call will @@ -6668,7 +6671,7 @@ int getpeersec(int fd, char **ret) { if (isempty(s)) { free(s); - return -ENOTSUP; + return -EOPNOTSUPP; } *ret = s; @@ -8122,3 +8125,44 @@ void cmsg_close_all(struct msghdr *mh) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int)); } + +int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { + struct stat buf; + int ret; + + ret = renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE); + if (ret >= 0) + return 0; + + /* Even though renameat2() exists since Linux 3.15, btrfs added + * support for it later. If it is not implemented, fallback to another + * method. */ + if (errno != EINVAL) + return -errno; + + /* The link()/unlink() fallback does not work on directories. But + * renameat() without RENAME_NOREPLACE gives the same semantics on + * directories, except when newpath is an *empty* directory. This is + * good enough. */ + ret = fstatat(olddirfd, oldpath, &buf, AT_SYMLINK_NOFOLLOW); + if (ret >= 0 && S_ISDIR(buf.st_mode)) { + ret = renameat(olddirfd, oldpath, newdirfd, newpath); + return ret >= 0 ? 0 : -errno; + } + + /* If it is not a directory, use the link()/unlink() fallback. */ + ret = linkat(olddirfd, oldpath, newdirfd, newpath, 0); + if (ret < 0) + return -errno; + + ret = unlinkat(olddirfd, oldpath, 0); + if (ret < 0) { + /* backup errno before the following unlinkat() alters it */ + ret = errno; + (void) unlinkat(newdirfd, newpath, 0); + errno = ret; + return -errno; + } + + return 0; +}