(void) rm_rf_dangerous(i->final_path, false, true, false);
}
- if (renameat2(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path, RENAME_NOREPLACE) < 0)
- return log_error_errno(errno, "Failed to move image into place: %m");
+ r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to move image into place: %m");
free(i->temp_path);
i->temp_path = NULL;
(void) rm_rf_dangerous(i->final_path, false, true, false);
}
- if (renameat2(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path, RENAME_NOREPLACE) < 0)
- return log_error_errno(errno, "Failed to move image into place: %m");
+ r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to move image into place: %m");
free(i->temp_path);
i->temp_path = NULL;
if (r < 0)
goto finish;
- r = renameat2(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path, RENAME_NOREPLACE);
+ r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
if (r < 0) {
- r = log_error_errno(errno, "Failed to move RAW file into place: %m");
+ log_error_errno(r, "Failed to move RAW file into place: %m");
goto finish;
}
if (r < 0)
goto finish;
- if (renameat2(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path, RENAME_NOREPLACE) < 0) {
- r = log_error_errno(errno, "Failed to rename to final image name: %m");
+ r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
+ if (r < 0) {
+ log_error_errno(r, "Failed to rename to final image name: %m");
goto finish;
}
if (r < 0)
return r;
- if (renameat2(AT_FDCWD, t, AT_FDCWD, to, replace ? 0 : RENAME_NOREPLACE) < 0) {
- unlink_noerrno(t);
- return -errno;
+ if (replace) {
+ r = renameat(AT_FDCWD, t, AT_FDCWD, to);
+ if (r < 0)
+ r = -errno;
+ } else
+ r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to);
+ if (r < 0) {
+ (void) unlink_noerrno(t);
+ return r;
}
return 0;
if (!nn)
return -ENOMEM;
- if (renameat2(AT_FDCWD, i->path, AT_FDCWD, new_path, RENAME_NOREPLACE) < 0)
- return -errno;
+ r = rename_noreplace(AT_FDCWD, i->path, AT_FDCWD, new_path);
+ if (r < 0)
+ return r;
/* Restore the immutable bit, if it was set before */
if (file_attr & FS_IMMUTABLE_FL)
goto fail;
}
- if (renameat2(AT_FDCWD, tmp, AT_FDCWD, "/var/lib/machines.raw", RENAME_NOREPLACE) < 0) {
- r = sd_bus_error_set_errnof(error, errno, "Failed to move /var/lib/machines.raw into place: %m");
+ r = rename_noreplace(AT_FDCWD, tmp, AT_FDCWD, "/var/lib/machines.raw");
+ if (r < 0) {
+ sd_bus_error_set_errnof(error, r, "Failed to move /var/lib/machines.raw into place: %m");
goto fail;
}
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;
+}
int syslog_parse_priority(const char **p, int *priority, bool with_facility);
void cmsg_close_all(struct msghdr *mh);
+
+int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);