aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2020-12-17 15:05:14 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2020-12-17 15:05:14 +0100
commit4bc59a4cf73f1dd9326d150bf6654b643e740fe2 (patch)
tree0ec7900f236995003b4ba19b3e19d1e3e74a0ca6
parentcd48f071173c2b24ae49f2b6eb35a76734f2d86b (diff)
downloadbusybox-w32-4bc59a4cf73f1dd9326d150bf6654b643e740fe2.tar.gz
busybox-w32-4bc59a4cf73f1dd9326d150bf6654b643e740fe2.tar.bz2
busybox-w32-4bc59a4cf73f1dd9326d150bf6654b643e740fe2.zip
mount: fix a race when a free loop device is snatched under us by another mount.
function old new delta set_loop 850 809 -41 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--libbb/loop.c138
1 files changed, 73 insertions, 65 deletions
diff --git a/libbb/loop.c b/libbb/loop.c
index 85b2724e5..153990998 100644
--- a/libbb/loop.c
+++ b/libbb/loop.c
@@ -98,9 +98,7 @@ int FAST_FUNC get_free_loop(void)
98 98
99/* Returns opened fd to the loop device, <0 on error. 99/* Returns opened fd to the loop device, <0 on error.
100 * *device is loop device to use, or if *device==NULL finds a loop device to 100 * *device is loop device to use, or if *device==NULL finds a loop device to
101 * mount it on and sets *device to a strdup of that loop device name. This 101 * mount it on and sets *device to a strdup of that loop device name.
102 * search will re-use an existing loop device already bound to that
103 * file/offset if it finds one.
104 */ 102 */
105int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offset, 103int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offset,
106 unsigned long long sizelimit, unsigned flags) 104 unsigned long long sizelimit, unsigned flags)
@@ -109,9 +107,7 @@ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offse
109 char *try; 107 char *try;
110 bb_loop_info loopinfo; 108 bb_loop_info loopinfo;
111 struct stat statbuf; 109 struct stat statbuf;
112 int i, dfd, ffd, mode, rc; 110 int i, lfd, ffd, mode, rc;
113
114 rc = dfd = -1;
115 111
116 /* Open the file. Barf if this doesn't work. */ 112 /* Open the file. Barf if this doesn't work. */
117 mode = (flags & BB_LO_FLAGS_READ_ONLY) ? O_RDONLY : O_RDWR; 113 mode = (flags & BB_LO_FLAGS_READ_ONLY) ? O_RDONLY : O_RDWR;
@@ -127,24 +123,23 @@ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offse
127 123
128 try = *device; 124 try = *device;
129 if (!try) { 125 if (!try) {
126 get_free_loopN:
130 i = get_free_loop(); 127 i = get_free_loop();
131 if (i == -2) { /* no /dev/loop-control */
132 i = 0;
133 try = dev;
134 goto old_style;
135 }
136 if (i == -1) { 128 if (i == -1) {
137 close(ffd); 129 close(ffd);
138 return -1; /* no free loop devices */ 130 return -1; /* no free loop devices */
139 } 131 }
140 try = *device = xasprintf(LOOP_FORMAT, i); 132 if (i >= 0) {
141 goto try_to_open; 133 try = xasprintf(LOOP_FORMAT, i);
134 goto open_lfd;
135 }
136 /* i == -2: no /dev/loop-control. Do an old-style search for a free device */
137 try = dev;
142 } 138 }
143 139
144 old_style: 140 /* Find a loop device */
145 /* Find a loop device. */ 141 /* 0xfffff is a max possible minor number in Linux circa 2010 */
146 /* 1048575 (0xfffff) is a max possible minor number in Linux circa 2010 */ 142 for (i = 0; i <= 0xfffff; i++) {
147 for (i = 0; rc && i < 1048576; i++) {
148 sprintf(dev, LOOP_FORMAT, i); 143 sprintf(dev, LOOP_FORMAT, i);
149 144
150 IF_FEATURE_MOUNT_LOOP_CREATE(errno = 0;) 145 IF_FEATURE_MOUNT_LOOP_CREATE(errno = 0;)
@@ -153,72 +148,85 @@ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offse
153 && errno == ENOENT 148 && errno == ENOENT
154 && try == dev 149 && try == dev
155 ) { 150 ) {
156 /* Node doesn't exist, try to create it. */ 151 /* Node doesn't exist, try to create it */
157 if (mknod(dev, S_IFBLK|0644, makedev(7, i)) == 0) 152 if (mknod(dev, S_IFBLK|0644, makedev(7, i)) == 0)
158 goto try_to_open; 153 goto open_lfd;
159 } 154 }
160 /* Ran out of block devices, return failure. */ 155 /* Ran out of block devices, return failure */
161 rc = -1; 156 rc = -1;
162 break; 157 break;
163 } 158 }
164 try_to_open: 159 open_lfd:
165 /* Open the sucker and check its loopiness. */ 160 /* Open the sucker and check its loopiness */
166 dfd = open(try, mode); 161 lfd = rc = open(try, mode);
167 if (dfd < 0 && errno == EROFS) { 162 if (lfd < 0 && errno == EROFS) {
168 mode = O_RDONLY; 163 mode = O_RDONLY;
169 dfd = open(try, mode); 164 lfd = rc = open(try, mode);
170 } 165 }
171 if (dfd < 0) { 166 if (lfd < 0) {
172 if (errno == ENXIO) { 167 if (errno == ENXIO) {
173 /* Happens if loop module is not loaded */ 168 /* Happens if loop module is not loaded */
174 rc = -1; 169 /* rc is -1; */
175 break; 170 break;
176 } 171 }
177 goto try_again; 172 goto try_next_loopN;
178 } 173 }
179 174
180 rc = ioctl(dfd, BB_LOOP_GET_STATUS, &loopinfo); 175 rc = ioctl(lfd, BB_LOOP_GET_STATUS, &loopinfo);
181 176
182 /* If device is free, claim it. */ 177 /* If device is free, try to claim it */
183 if (rc && errno == ENXIO) { 178 if (rc && errno == ENXIO) {
184 /* Associate free loop device with file. */ 179 /* Associate free loop device with file */
185 if (ioctl(dfd, LOOP_SET_FD, ffd) == 0) { 180 if (ioctl(lfd, LOOP_SET_FD, ffd)) {
186 memset(&loopinfo, 0, sizeof(loopinfo)); 181 /* Ouch. Are we racing with other mount? */
187 safe_strncpy((char *)loopinfo.lo_file_name, file, LO_NAME_SIZE); 182 if (!*device /* yes */
188 loopinfo.lo_offset = offset; 183 && try != dev /* tried a _kernel-offered_ loopN? */
189 loopinfo.lo_sizelimit = sizelimit; 184 ) {
190 /* 185 free(try);
191 * Used by mount to set LO_FLAGS_AUTOCLEAR. 186 close(lfd);
192 * LO_FLAGS_READ_ONLY is not set because RO is controlled by open type of the file. 187//TODO: add "if (--failcount != 0) ..."?
193 * Note that closing LO_FLAGS_AUTOCLEARed dfd before mount 188 goto get_free_loopN;
194 * is wrong (would free the loop device!)
195 */
196 loopinfo.lo_flags = (flags & ~BB_LO_FLAGS_READ_ONLY);
197 rc = ioctl(dfd, BB_LOOP_SET_STATUS, &loopinfo);
198 if (rc != 0 && (loopinfo.lo_flags & BB_LO_FLAGS_AUTOCLEAR)) {
199 /* Old kernel, does not support LO_FLAGS_AUTOCLEAR? */
200 /* (this code path is not tested) */
201 loopinfo.lo_flags -= BB_LO_FLAGS_AUTOCLEAR;
202 rc = ioctl(dfd, BB_LOOP_SET_STATUS, &loopinfo);
203 }
204 if (rc != 0) {
205 ioctl(dfd, LOOP_CLR_FD, 0); // actually, 0 param is unnecessary
206 } 189 }
190 goto try_next_loopN;
207 } 191 }
208 } else { 192 memset(&loopinfo, 0, sizeof(loopinfo));
209 rc = -1; 193 safe_strncpy((char *)loopinfo.lo_file_name, file, LO_NAME_SIZE);
210 } 194 loopinfo.lo_offset = offset;
211 if (rc != 0) { 195 loopinfo.lo_sizelimit = sizelimit;
212 close(dfd); 196 /*
197 * Used by mount to set LO_FLAGS_AUTOCLEAR.
198 * LO_FLAGS_READ_ONLY is not set because RO is controlled by open type of the file.
199 * Note that closing LO_FLAGS_AUTOCLEARed lfd before mount
200 * is wrong (would free the loop device!)
201 */
202 loopinfo.lo_flags = (flags & ~BB_LO_FLAGS_READ_ONLY);
203 rc = ioctl(lfd, BB_LOOP_SET_STATUS, &loopinfo);
204 if (rc != 0 && (loopinfo.lo_flags & BB_LO_FLAGS_AUTOCLEAR)) {
205 /* Old kernel, does not support LO_FLAGS_AUTOCLEAR? */
206 /* (this code path is not tested) */
207 loopinfo.lo_flags -= BB_LO_FLAGS_AUTOCLEAR;
208 rc = ioctl(lfd, BB_LOOP_SET_STATUS, &loopinfo);
209 }
210 if (rc == 0) {
211 /* SUCCESS! */
212 if (try != dev) /* tried a kernel-offered free loopN? */
213 *device = try; /* malloced */
214 if (!*device) /* was looping in search of free "/dev/loopN"? */
215 *device = xstrdup(dev);
216 rc = lfd; /* return this */
217 break;
218 }
219 /* failure, undo LOOP_SET_FD */
220 ioctl(lfd, LOOP_CLR_FD, 0); // actually, 0 param is unnecessary
213 } 221 }
214 try_again: 222 /* else: device is not free (rc == 0) or error other than ENXIO */
215 if (*device) break; 223 close(lfd);
216 } 224 try_next_loopN:
225 rc = -1;
226 if (*device) /* was looking for a particular "/dev/loopN"? */
227 break; /* yes, do not try other names */
228 } /* for() */
229
217 close(ffd); 230 close(ffd);
218 if (rc == 0) {
219 if (!*device)
220 *device = xstrdup(dev);
221 return dfd;
222 }
223 return rc; 231 return rc;
224} 232}