aboutsummaryrefslogtreecommitdiff
path: root/libbb
diff options
context:
space:
mode:
Diffstat (limited to 'libbb')
-rw-r--r--libbb/Config.src22
-rw-r--r--libbb/lineedit.c24
-rw-r--r--libbb/loop.c175
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
373choice
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
384config LOOP_CONFIGURE
385 bool "use LOOP_CONFIGURE, needs kernel >= 5.8"
386
387config NO_LOOP_CONFIGURE
388 bool "use LOOP_SET_FD + LOOP_SET_STATUS"
389
390config TRY_LOOP_CONFIGURE
391 bool "try LOOP_CONFIGURE, fall back to LOOP_SET_FD + LOOP_SET_STATUS"
392
393endchoice
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
252static 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
260static NOINLINE const char *get_homedir_or_NULL(void) 252static 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
271static 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
279static size_t load_string(const char *src) 281static 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
99static 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
115struct 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
123static 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;