diff options
Diffstat (limited to 'libbb')
-rw-r--r-- | libbb/Config.src | 22 | ||||
-rw-r--r-- | libbb/lineedit.c | 24 | ||||
-rw-r--r-- | libbb/loop.c | 175 |
3 files changed, 148 insertions, 73 deletions
diff --git a/libbb/Config.src b/libbb/Config.src index 66a3ffa23..b980f19a9 100644 --- a/libbb/Config.src +++ b/libbb/Config.src | |||
@@ -369,3 +369,25 @@ config UNICODE_PRESERVE_BROKEN | |||
369 | For example, this means that entering 'l', 's', ' ', 0xff, [Enter] | 369 | For example, this means that entering 'l', 's', ' ', 0xff, [Enter] |
370 | at shell prompt will list file named 0xff (single char name | 370 | at shell prompt will list file named 0xff (single char name |
371 | with char value 255), not file named '?'. | 371 | with char value 255), not file named '?'. |
372 | |||
373 | choice | ||
374 | prompt "Use LOOP_CONFIGURE for losetup and loop mounts" | ||
375 | default TRY_LOOP_CONFIGURE | ||
376 | help | ||
377 | LOOP_CONFIGURE is added to Linux 5.8 | ||
378 | https://lwn.net/Articles/820408/ | ||
379 | This allows userspace to completely setup a loop device with a single | ||
380 | ioctl, removing the in-between state where the device can be partially | ||
381 | configured - eg the loop device has a backing file associated with it, | ||
382 | but is reading from the wrong offset. | ||
383 | |||
384 | config LOOP_CONFIGURE | ||
385 | bool "use LOOP_CONFIGURE, needs kernel >= 5.8" | ||
386 | |||
387 | config NO_LOOP_CONFIGURE | ||
388 | bool "use LOOP_SET_FD + LOOP_SET_STATUS" | ||
389 | |||
390 | config TRY_LOOP_CONFIGURE | ||
391 | bool "try LOOP_CONFIGURE, fall back to LOOP_SET_FD + LOOP_SET_STATUS" | ||
392 | |||
393 | endchoice | ||
diff --git a/libbb/lineedit.c b/libbb/lineedit.c index b4950688e..bee423553 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c | |||
@@ -249,14 +249,6 @@ static void get_user_strings(void) | |||
249 | } | 249 | } |
250 | } | 250 | } |
251 | 251 | ||
252 | static const char *get_username_str(void) | ||
253 | { | ||
254 | if (!got_user_strings) | ||
255 | get_user_strings(); | ||
256 | return user_buf ? user_buf : ""; | ||
257 | /* btw, bash uses "I have no name!" string if uid has no entry */ | ||
258 | } | ||
259 | |||
260 | static NOINLINE const char *get_homedir_or_NULL(void) | 252 | static NOINLINE const char *get_homedir_or_NULL(void) |
261 | { | 253 | { |
262 | const char *home; | 254 | const char *home; |
@@ -275,6 +267,16 @@ static NOINLINE const char *get_homedir_or_NULL(void) | |||
275 | } | 267 | } |
276 | #endif | 268 | #endif |
277 | 269 | ||
270 | #if ENABLE_FEATURE_EDITING_FANCY_PROMPT | ||
271 | static const char *get_username_str(void) | ||
272 | { | ||
273 | if (!got_user_strings) | ||
274 | get_user_strings(); | ||
275 | return user_buf ? user_buf : ""; | ||
276 | /* btw, bash uses "I have no name!" string if uid has no entry */ | ||
277 | } | ||
278 | #endif | ||
279 | |||
278 | #if ENABLE_UNICODE_SUPPORT | 280 | #if ENABLE_UNICODE_SUPPORT |
279 | static size_t load_string(const char *src) | 281 | static size_t load_string(const char *src) |
280 | { | 282 | { |
@@ -2151,13 +2153,13 @@ static void parse_and_put_prompt(const char *prmt_ptr) | |||
2151 | case 'W': /* basename of cur dir */ | 2153 | case 'W': /* basename of cur dir */ |
2152 | if (!cwd_buf) { | 2154 | if (!cwd_buf) { |
2153 | const char *home; | 2155 | const char *home; |
2154 | #if ENABLE_SHELL_ASH | 2156 | # if EDITING_HAS_sh_get_var |
2155 | cwd_buf = state->sh_get_var | 2157 | cwd_buf = state->sh_get_var |
2156 | ? xstrdup(state->sh_get_var("PWD")) | 2158 | ? xstrdup(state->sh_get_var("PWD")) |
2157 | : xrealloc_getcwd_or_warn(NULL); | 2159 | : xrealloc_getcwd_or_warn(NULL); |
2158 | #else | 2160 | # else |
2159 | cwd_buf = xrealloc_getcwd_or_warn(NULL); | 2161 | cwd_buf = xrealloc_getcwd_or_warn(NULL); |
2160 | #endif | 2162 | # endif |
2161 | if (!cwd_buf) | 2163 | if (!cwd_buf) |
2162 | cwd_buf = (char *)bb_msg_unknown; | 2164 | cwd_buf = (char *)bb_msg_unknown; |
2163 | else if ((home = get_homedir_or_NULL()) != NULL && home[0]) { | 2165 | else if ((home = get_homedir_or_NULL()) != NULL && home[0]) { |
diff --git a/libbb/loop.c b/libbb/loop.c index cb8fa2442..a0c5d0259 100644 --- a/libbb/loop.c +++ b/libbb/loop.c | |||
@@ -71,7 +71,7 @@ int FAST_FUNC del_loop(const char *device) | |||
71 | 71 | ||
72 | fd = open(device, O_RDONLY); | 72 | fd = open(device, O_RDONLY); |
73 | if (fd < 0) | 73 | if (fd < 0) |
74 | return 1; | 74 | return fd; /* -1 */ |
75 | rc = ioctl(fd, LOOP_CLR_FD, 0); | 75 | rc = ioctl(fd, LOOP_CLR_FD, 0); |
76 | close(fd); | 76 | close(fd); |
77 | 77 | ||
@@ -96,6 +96,97 @@ int FAST_FUNC get_free_loop(void) | |||
96 | return loopdevno; /* can be -1 if error */ | 96 | return loopdevno; /* can be -1 if error */ |
97 | } | 97 | } |
98 | 98 | ||
99 | static int get_next_free_loop(char *dev, int id) | ||
100 | { | ||
101 | int loopdevno; | ||
102 | |||
103 | loopdevno = get_free_loop(); | ||
104 | if (loopdevno != -1) { | ||
105 | /* loopdevno is -2 (use id) or >= 0 (use id = loopdevno): */ | ||
106 | if (loopdevno >= 0) | ||
107 | id = loopdevno; | ||
108 | sprintf(dev, LOOP_FORMAT, id); | ||
109 | } | ||
110 | return loopdevno; | ||
111 | } | ||
112 | |||
113 | #if ENABLE_TRY_LOOP_CONFIGURE || ENABLE_LOOP_CONFIGURE | ||
114 | # define LOOP_CONFIGURE 0x4C0A | ||
115 | struct bb_loop_config { | ||
116 | uint32_t fd; | ||
117 | uint32_t block_size; | ||
118 | struct loop_info64 info; | ||
119 | uint64_t __reserved[8]; | ||
120 | }; | ||
121 | #endif | ||
122 | |||
123 | static int set_loopdev_params(int lfd, | ||
124 | int ffd, const char *file, | ||
125 | unsigned long long offset, | ||
126 | unsigned long long sizelimit, | ||
127 | unsigned flags) | ||
128 | { | ||
129 | int rc; | ||
130 | #if ENABLE_TRY_LOOP_CONFIGURE || ENABLE_LOOP_CONFIGURE | ||
131 | struct bb_loop_config lconfig; | ||
132 | # define loopinfo lconfig.info | ||
133 | #else | ||
134 | bb_loop_info loopinfo; | ||
135 | #endif | ||
136 | |||
137 | rc = ioctl(lfd, BB_LOOP_GET_STATUS, &loopinfo); | ||
138 | |||
139 | /* If device is free, try to claim it */ | ||
140 | if (rc && errno == ENXIO) { | ||
141 | #if ENABLE_TRY_LOOP_CONFIGURE || ENABLE_LOOP_CONFIGURE | ||
142 | memset(&lconfig, 0, sizeof(lconfig)); | ||
143 | #else | ||
144 | memset(&loopinfo, 0, sizeof(loopinfo)); | ||
145 | #endif | ||
146 | safe_strncpy((char *)loopinfo.lo_file_name, file, LO_NAME_SIZE); | ||
147 | loopinfo.lo_offset = offset; | ||
148 | loopinfo.lo_sizelimit = sizelimit; | ||
149 | /* | ||
150 | * LO_FLAGS_READ_ONLY is not set because RO is controlled | ||
151 | * by open type of the lfd. | ||
152 | */ | ||
153 | loopinfo.lo_flags = (flags & ~BB_LO_FLAGS_READ_ONLY); | ||
154 | |||
155 | #if ENABLE_TRY_LOOP_CONFIGURE || ENABLE_LOOP_CONFIGURE | ||
156 | lconfig.fd = ffd; | ||
157 | rc = ioctl(lfd, LOOP_CONFIGURE, &lconfig); | ||
158 | if (rc == 0) | ||
159 | return rc; /* SUCCESS! */ | ||
160 | # if ENABLE_TRY_LOOP_CONFIGURE | ||
161 | if (errno != EINVAL) | ||
162 | return rc; /* error other than old kernel */ | ||
163 | /* Old kernel, fall through into old way to do it: */ | ||
164 | # endif | ||
165 | #endif | ||
166 | #if ENABLE_TRY_LOOP_CONFIGURE || ENABLE_NO_LOOP_CONFIGURE | ||
167 | /* Associate free loop device with file */ | ||
168 | rc = ioctl(lfd, LOOP_SET_FD, ffd); | ||
169 | if (rc != 0) { | ||
170 | /* Ouch... race: the device already has a fd */ | ||
171 | return rc; | ||
172 | } | ||
173 | rc = ioctl(lfd, BB_LOOP_SET_STATUS, &loopinfo); | ||
174 | if (rc != 0 && (loopinfo.lo_flags & BB_LO_FLAGS_AUTOCLEAR)) { | ||
175 | /* Old kernel, does not support LO_FLAGS_AUTOCLEAR? */ | ||
176 | /* (this code path is not tested) */ | ||
177 | loopinfo.lo_flags -= BB_LO_FLAGS_AUTOCLEAR; | ||
178 | rc = ioctl(lfd, BB_LOOP_SET_STATUS, &loopinfo); | ||
179 | } | ||
180 | if (rc == 0) | ||
181 | return rc; /* SUCCESS! */ | ||
182 | /* failure, undo LOOP_SET_FD */ | ||
183 | ioctl(lfd, LOOP_CLR_FD, 0); // actually, 0 param is unnecessary | ||
184 | #endif | ||
185 | } | ||
186 | return -1; | ||
187 | #undef loopinfo | ||
188 | } | ||
189 | |||
99 | /* Returns opened fd to the loop device, <0 on error. | 190 | /* 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 | 191 | * *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. | 192 | * mount it on and sets *device to a strdup of that loop device name. |
@@ -105,7 +196,6 @@ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offse | |||
105 | { | 196 | { |
106 | char dev[LOOP_NAMESIZE]; | 197 | char dev[LOOP_NAMESIZE]; |
107 | char *try; | 198 | char *try; |
108 | bb_loop_info loopinfo; | ||
109 | struct stat statbuf; | 199 | struct stat statbuf; |
110 | int i, lfd, ffd, mode, rc; | 200 | int i, lfd, ffd, mode, rc; |
111 | 201 | ||
@@ -123,30 +213,27 @@ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offse | |||
123 | 213 | ||
124 | try = *device; | 214 | try = *device; |
125 | if (!try) { | 215 | if (!try) { |
126 | get_free_loopN: | ||
127 | i = get_free_loop(); | ||
128 | if (i == -1) { | ||
129 | close(ffd); | ||
130 | return -1; /* no free loop devices */ | ||
131 | } | ||
132 | if (i >= 0) { | ||
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; | 216 | try = dev; |
138 | } | 217 | } |
139 | 218 | ||
140 | /* Find a loop device */ | 219 | /* Find a loop device */ |
141 | /* 0xfffff is a max possible minor number in Linux circa 2010 */ | 220 | /* 0xfffff is a max possible minor number in Linux circa 2010 */ |
142 | for (i = 0; i <= 0xfffff; i++) { | 221 | for (i = 0; i <= 0xfffff; i++) { |
143 | sprintf(dev, LOOP_FORMAT, i); | 222 | if (!*device) { |
223 | rc = get_next_free_loop(dev, i); | ||
224 | if (rc == -1) | ||
225 | break; /* no free loop devices (or other error in LOOP_CTL_GET_FREE) */ | ||
226 | if (rc >= 0) | ||
227 | /* /dev/loop-control gave us the next free /dev/loopN */ | ||
228 | goto open_lfd; | ||
229 | /* else: sequential /dev/loopN, needs to be tested/maybe_created */ | ||
230 | } | ||
144 | 231 | ||
145 | IF_FEATURE_MOUNT_LOOP_CREATE(errno = 0;) | 232 | IF_FEATURE_MOUNT_LOOP_CREATE(errno = 0;) |
146 | if (stat(try, &statbuf) != 0 || !S_ISBLK(statbuf.st_mode)) { | 233 | if (stat(try, &statbuf) != 0 || !S_ISBLK(statbuf.st_mode)) { |
147 | if (ENABLE_FEATURE_MOUNT_LOOP_CREATE | 234 | if (ENABLE_FEATURE_MOUNT_LOOP_CREATE |
148 | && errno == ENOENT | 235 | && errno == ENOENT |
149 | && try == dev | 236 | && (!*device) |
150 | ) { | 237 | ) { |
151 | /* Node doesn't exist, try to create it */ | 238 | /* Node doesn't exist, try to create it */ |
152 | if (mknod(dev, S_IFBLK|0644, makedev(7, i)) == 0) | 239 | if (mknod(dev, S_IFBLK|0644, makedev(7, i)) == 0) |
@@ -172,55 +259,19 @@ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offse | |||
172 | goto try_next_loopN; | 259 | goto try_next_loopN; |
173 | } | 260 | } |
174 | 261 | ||
175 | rc = ioctl(lfd, BB_LOOP_GET_STATUS, &loopinfo); | 262 | rc = set_loopdev_params(lfd, ffd, file, offset, sizelimit, flags); |
176 | 263 | if (rc == 0) { | |
177 | /* If device is free, try to claim it */ | 264 | /* SUCCESS! */ |
178 | if (rc && errno == ENXIO) { | 265 | if (!*device) |
179 | /* Associate free loop device with file */ | 266 | *device = xstrdup(dev); |
180 | if (ioctl(lfd, LOOP_SET_FD, ffd)) { | 267 | /* Note: mount asks for LO_FLAGS_AUTOCLEAR loopdev. |
181 | /* Ouch. Are we racing with other mount? */ | 268 | * Closing LO_FLAGS_AUTOCLEARed lfd before mount |
182 | if (!*device /* yes */ | 269 | * is wrong (would free the loop device!), |
183 | && try != dev /* tried a _kernel-offered_ loopN? */ | 270 | * this is why we return without closing it. |
184 | ) { | ||
185 | free(try); | ||
186 | close(lfd); | ||
187 | //TODO: add "if (--failcount != 0) ..."? | ||
188 | goto get_free_loopN; | ||
189 | } | ||
190 | goto close_and_try_next_loopN; | ||
191 | } | ||
192 | memset(&loopinfo, 0, sizeof(loopinfo)); | ||
193 | safe_strncpy((char *)loopinfo.lo_file_name, file, LO_NAME_SIZE); | ||
194 | loopinfo.lo_offset = offset; | ||
195 | loopinfo.lo_sizelimit = sizelimit; | ||
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 | */ | 271 | */ |
202 | loopinfo.lo_flags = (flags & ~BB_LO_FLAGS_READ_ONLY); | 272 | rc = lfd; /* return this */ |
203 | rc = ioctl(lfd, BB_LOOP_SET_STATUS, &loopinfo); | 273 | break; |
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 | ||
221 | } | 274 | } |
222 | /* else: device is not free (rc == 0) or error other than ENXIO */ | ||
223 | close_and_try_next_loopN: | ||
224 | close(lfd); | 275 | close(lfd); |
225 | try_next_loopN: | 276 | try_next_loopN: |
226 | rc = -1; | 277 | rc = -1; |