]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/loop-util.c
util-lib: split out image dissecting code and loopback code from nspawn
[thirdparty/systemd.git] / src / shared / loop-util.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2016 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <fcntl.h>
21 #include <linux/loop.h>
22 #include <sys/ioctl.h>
23 #include <sys/stat.h>
24
25 #include "alloc-util.h"
26 #include "fd-util.h"
27 #include "loop-util.h"
28
29 int loop_device_make(int fd, int open_flags, LoopDevice **ret) {
30 const struct loop_info64 info = {
31 .lo_flags = LO_FLAGS_AUTOCLEAR|LO_FLAGS_PARTSCAN|(open_flags == O_RDONLY ? LO_FLAGS_READ_ONLY : 0),
32 };
33
34 _cleanup_close_ int control = -1, loop = -1;
35 _cleanup_free_ char *loopdev = NULL;
36 struct stat st;
37 LoopDevice *d;
38 int nr;
39
40 assert(fd >= 0);
41 assert(ret);
42 assert(IN_SET(open_flags, O_RDWR, O_RDONLY));
43
44 if (fstat(fd, &st) < 0)
45 return -errno;
46
47 if (S_ISBLK(st.st_mode)) {
48 int copy;
49
50 /* If this is already a block device, store a copy of the fd as it is */
51
52 copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
53 if (copy < 0)
54 return -errno;
55
56 d = new0(LoopDevice, 1);
57 if (!d)
58 return -ENOMEM;
59
60 *d = (LoopDevice) {
61 .fd = copy,
62 .nr = -1,
63 };
64
65 *ret = d;
66
67 return 0;
68 }
69
70 if (!S_ISREG(st.st_mode))
71 return -EINVAL;
72
73 control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
74 if (control < 0)
75 return -errno;
76
77 nr = ioctl(control, LOOP_CTL_GET_FREE);
78 if (nr < 0)
79 return -errno;
80
81 if (asprintf(&loopdev, "/dev/loop%i", nr) < 0)
82 return -ENOMEM;
83
84 loop = open(loopdev, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|open_flags);
85 if (loop < 0)
86 return -errno;
87
88 if (ioctl(loop, LOOP_SET_FD, fd) < 0)
89 return -errno;
90
91 if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0)
92 return -errno;
93
94 d = new(LoopDevice, 1);
95 if (!d)
96 return -ENOMEM;
97
98 *d = (LoopDevice) {
99 .fd = loop,
100 .node = loopdev,
101 .nr = nr,
102 };
103
104 loop = -1;
105 loopdev = NULL;
106
107 *ret = d;
108
109 return (*ret)->fd;
110 }
111
112 int loop_device_make_by_path(const char *path, int open_flags, LoopDevice **ret) {
113 _cleanup_close_ int fd = -1;
114
115 assert(path);
116 assert(ret);
117 assert(IN_SET(open_flags, O_RDWR, O_RDONLY));
118
119 fd = open(path, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|open_flags);
120 if (fd < 0)
121 return -errno;
122
123 return loop_device_make(fd, open_flags, ret);
124 }
125
126 LoopDevice* loop_device_unref(LoopDevice *d) {
127 if (!d)
128 return NULL;
129
130 if (d->fd >= 0) {
131
132 if (d->nr >= 0) {
133 if (ioctl(d->fd, LOOP_CLR_FD) < 0)
134 log_debug_errno(errno, "Failed to clear loop device: %m");
135
136 }
137
138 safe_close(d->fd);
139 }
140
141 if (d->nr >= 0) {
142 _cleanup_close_ int control = -1;
143
144 control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
145 if (control < 0)
146 log_debug_errno(errno, "Failed to open loop control device: %m");
147 else {
148 if (ioctl(control, LOOP_CTL_REMOVE, d->nr) < 0)
149 log_debug_errno(errno, "Failed to remove loop device: %m");
150 }
151 }
152
153 free(d->node);
154 free(d);
155
156 return NULL;
157 }