util: add rename_noreplace
renameat2() exists since Linux 3.15 but btrfs support for the flag RENAME_NOREPLACE was added later. This patch implements a fallback when renameat2() returns EINVAL. EINVAL is the error returned when the filesystem does not support one of the flags.
This commit is contained in:
parent
27cc6f166b
commit
f85ef957e6
|
@ -245,8 +245,9 @@ static int raw_import_finish(RawImport *i) {
|
|||
(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;
|
||||
|
|
|
@ -201,8 +201,9 @@ static int tar_import_finish(TarImport *i) {
|
|||
(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;
|
||||
|
|
|
@ -384,9 +384,9 @@ static void raw_pull_job_on_finished(PullJob *j) {
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -281,8 +281,9 @@ static void tar_pull_job_on_finished(PullJob *j) {
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -404,9 +404,15 @@ int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace
|
|||
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;
|
||||
|
|
|
@ -440,8 +440,9 @@ int image_rename(Image *i, const char *new_name) {
|
|||
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)
|
||||
|
|
|
@ -140,8 +140,9 @@ static int setup_machine_raw(uint64_t size, sd_bus_error *error) {
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -8122,3 +8122,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;
|
||||
}
|
||||
|
|
|
@ -1081,3 +1081,5 @@ void sigkill_wait(pid_t *pid);
|
|||
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);
|
||||
|
|
Loading…
Reference in New Issue