]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/shared/loop-util.c
man: fix incorrectly placed full stop
[thirdparty/systemd.git] / src / shared / loop-util.c
index 6cb45f1f1bc15db75a2059d1e99dc785c317b4db..4a593b05f3c90073b6fd87e8ad60d66d040b7562 100644 (file)
@@ -14,6 +14,7 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
+#include "errno-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "loop-util.h"
@@ -29,7 +30,7 @@ static void cleanup_clear_loop_close(int *fd) {
         }
 }
 
-int loop_device_make_full(
+int loop_device_make(
                 int fd,
                 int open_flags,
                 uint64_t offset,
@@ -157,16 +158,32 @@ int loop_device_make_full(
 
 int loop_device_make_by_path(const char *path, int open_flags, uint32_t loop_flags, LoopDevice **ret) {
         _cleanup_close_ int fd = -1;
+        int r;
 
         assert(path);
         assert(ret);
-        assert(IN_SET(open_flags, O_RDWR, O_RDONLY));
+        assert(open_flags < 0 || IN_SET(open_flags, O_RDWR, O_RDONLY));
 
-        fd = open(path, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|open_flags);
-        if (fd < 0)
-                return -errno;
+        /* Passing < 0 as open_flags here means we'll try to open the device writable if we can, retrying
+         * read-only if we cannot. */
 
-        return loop_device_make_full(fd, open_flags, 0, 0, loop_flags, ret);
+        fd = open(path, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|(open_flags >= 0 ? open_flags : O_RDWR));
+        if (fd < 0) {
+                r = -errno;
+
+                /* Retry read-only? */
+                if (open_flags >= 0 || !(ERRNO_IS_PRIVILEGE(r) || r == -EROFS))
+                        return r;
+
+                fd = open(path, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|O_RDONLY);
+                if (fd < 0)
+                        return r; /* Propagate original error */
+
+                open_flags = O_RDONLY;
+        } else if (open_flags < 0)
+                open_flags = O_RDWR;
+
+        return loop_device_make(fd, open_flags, 0, 0, loop_flags, ret);
 }
 
 LoopDevice* loop_device_unref(LoopDevice *d) {
@@ -174,6 +191,10 @@ LoopDevice* loop_device_unref(LoopDevice *d) {
                 return NULL;
 
         if (d->fd >= 0) {
+                /* Implicitly sync the device, since otherwise in-flight blocks might not get written */
+                if (fsync(d->fd) < 0)
+                        log_debug_errno(errno, "Failed to sync loop block device, ignoring: %m");
+
                 if (d->nr >= 0 && !d->relinquished) {
                         if (ioctl(d->fd, LOOP_CLR_FD) < 0)
                                 log_debug_errno(errno, "Failed to clear loop device: %m");
@@ -199,7 +220,7 @@ LoopDevice* loop_device_unref(LoopDevice *d) {
                                         log_warning_errno(errno, "Failed to remove device %s: %m", strna(d->node));
                                         break;
                                 }
-                                usleep(50 * USEC_PER_MSEC);
+                                (void) usleep(50 * USEC_PER_MSEC);
                         }
         }