chiark / gitweb /
util: add rename_noreplace
authorAlban Crequy <alban@endocode.com>
Tue, 10 Mar 2015 17:15:52 +0000 (18:15 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 10 Mar 2015 17:23:46 +0000 (18:23 +0100)
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.

src/import/import-raw.c
src/import/import-tar.c
src/import/pull-raw.c
src/import/pull-tar.c
src/shared/copy.c
src/shared/machine-image.c
src/shared/machine-pool.c
src/shared/util.c
src/shared/util.h

index 25b52f7cbd9e5394a25cb0ebf4a48542f092623b..7d1ac2afd700749cf6166b70f1d258050ab1ec6b 100644 (file)
@@ -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;
index dd95575660b6ebac2f4a2bc841e64507eb81b690..ef2345c7b93a268b8986bd000dc36969a04cb99d 100644 (file)
@@ -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;
index d1d77d598bc7111946624db25de61ecda1a365b7..c0c6d57eadee8d1224f02580dac0b5b0854b3e1b 100644 (file)
@@ -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;
                 }
 
index 16994e1c242044ecac3e8d892078fa932fc96617..58cafdd7891d93c3710260ae66402ea12117ef65 100644 (file)
@@ -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;
                 }
 
index 0239a58066ec3314dc004fde3a3a49cf84846e2b..f9ec6733bec7249f53fa87979616ec634a7bc91d 100644 (file)
@@ -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;
index 552847e0f0119890255d3244629dcecd3cbd8d41..00337e7c9fe03a6c208e0e16034d45cc5b9bcc71 100644 (file)
@@ -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)
index e7671a3c5ecfbb46868fd7e8023c1dc111454f63..0fae623944d9a705eb5e36bb80db2084aeef4381 100644 (file)
@@ -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;
         }
 
index b90e10ffb2f05726f0e1dd80cead5de5fd3f9ab0..55de83023d53c1e6c34226e532df505418473561 100644 (file)
@@ -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;
+}
index d2da3e2895e3fd1aba6c824129685b9f505024af..d229e1e68c95ac447462255fdf7907900f8b9482 100644 (file)
@@ -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);