From c6137ba51066d78a255f440ed0afc36639ea604a Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 4 Jan 2017 12:13:38 +0100 Subject: udhcp: tweak config order and menu item names All other applets are listed simply by their name, no reason why dumpleases doesn't do that. Group all udhcpd feature options directly after it. Put "NOT READY" into udhcpc6 item (some users actually tried to use it, and complained). Signed-off-by: Denys Vlasenko --- networking/udhcp/Config.src | 54 ++++++++++++++++++++++----------------------- networking/udhcp/d6_dhcpc.c | 2 +- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/networking/udhcp/Config.src b/networking/udhcp/Config.src index 90fb313b5..7bc13a719 100644 --- a/networking/udhcp/Config.src +++ b/networking/udhcp/Config.src @@ -6,29 +6,13 @@ INSERT config UDHCPD - bool "udhcp server (udhcpd)" + bool "udhcpd (DHCP server)" default y select PLATFORM_LINUX help udhcpd is a DHCP server geared primarily toward embedded systems, while striving to be fully functional and RFC compliant. -config DHCPRELAY - bool "dhcprelay" - default y - help - dhcprelay listens for dhcp requests on one or more interfaces - and forwards these requests to a different interface or dhcp - server. - -config DUMPLEASES - bool "Lease display utility (dumpleases)" - default y - help - dumpleases displays the leases written out by the udhcpd server. - Lease times are stored in the file by time remaining in lease, or - by the absolute time that it expires in seconds from epoch. - config FEATURE_UDHCPD_WRITE_LEASES_EARLY bool "Rewrite the lease file at every new acknowledge" default y @@ -61,8 +45,24 @@ config DHCPD_LEASES_FILE udhcpd stores addresses in a lease file. This is the absolute path of the file. Normally it is safe to leave it untouched. +config DUMPLEASES + bool "dumpleases" + default y + help + dumpleases displays the leases written out by the udhcpd. + Lease times are stored in the file by time remaining in lease, or + by the absolute time that it expires in seconds from epoch. + +config DHCPRELAY + bool "dhcprelay" + default y + help + dhcprelay listens for dhcp requests on one or more interfaces + and forwards these requests to a different interface or dhcp + server. + config UDHCPC - bool "udhcp client (udhcpc)" + bool "udhcpc (DHCP client)" default y select PLATFORM_LINUX help @@ -93,6 +93,15 @@ config FEATURE_UDHCPC_SANITIZEOPT they will be replaced with string "bad" when exporting to the environment. +config UDHCPC_DEFAULT_SCRIPT + string "Absolute path to config script" + default "/usr/share/udhcpc/default.script" + depends on UDHCPC + help + This script is called after udhcpc receives an answer. See + examples/udhcp for a working example. Normally it is safe + to leave this untouched. + config FEATURE_UDHCP_PORT bool "Enable '-P port' option for udhcpd and udhcpc" default n @@ -130,15 +139,6 @@ config FEATURE_UDHCP_8021Q If selected, both client and server will support passing of VLAN ID and priority via options 132 and 133 as per 802.1Q. -config UDHCPC_DEFAULT_SCRIPT - string "Absolute path to config script" - default "/usr/share/udhcpc/default.script" - depends on UDHCPC - help - This script is called after udhcpc receives an answer. See - examples/udhcp for a working example. Normally it is safe - to leave this untouched. - config UDHCPC_SLACK_FOR_BUGGY_SERVERS int "DHCP options slack buffer size" default 80 diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c index ddf3412a0..64339c9b5 100644 --- a/networking/udhcp/d6_dhcpc.c +++ b/networking/udhcp/d6_dhcpc.c @@ -12,7 +12,7 @@ */ //config:config UDHCPC6 -//config: bool "udhcp client for DHCPv6 (udhcpc6)" +//config: bool "udhcpc6 (DHCPv6 client, NOT READY)" //config: default n # not yet ready //config: depends on FEATURE_IPV6 //config: help -- cgit v1.2.3-55-g6feb From 9529115c4ce87d4faecd1c55e547162efe74d629 Mon Sep 17 00:00:00 2001 From: Kang-Che Sung Date: Wed, 4 Jan 2017 12:29:04 +0100 Subject: shell: clarify help text of CONFIG_{SH,BASH}_IS_* options Mention the behavior if user selects CONFIG_SH_IS_ASH but not CONFIG_ASH. We will be explicit that invocations like "busybox ash" will not work for such configuration. Also clarify help text of CONFIG_BASH_IS_* that bash compatibility in ash is not complete. (It shouldn't be anyway - ash can't support every bash quirk out there.) Signed-off-by: Kang-Che Sung Signed-off-by: Denys Vlasenko --- shell/Config.src | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/shell/Config.src b/shell/Config.src index 7f5f67050..9bd493fed 100644 --- a/shell/Config.src +++ b/shell/Config.src @@ -17,9 +17,19 @@ choice config SH_IS_ASH depends on !NOMMU bool "ash" + help + Choose ash to be the shell executed by 'sh' name. + The ash code will be built into busybox. If you don't select + "ash" choice (CONFIG_ASH), this shell may only be invoked by + the name 'sh' (and not 'ash'). config SH_IS_HUSH bool "hush" + help + Choose hush to be the shell executed by 'sh' name. + The hush code will be built into busybox. If you don't select + "hush" choice (CONFIG_HUSH), this shell may only be invoked by + the name 'sh' (and not 'hush'). config SH_IS_NONE bool "none" @@ -31,7 +41,8 @@ choice default BASH_IS_NONE help Choose which shell you want to be executed by 'bash' alias. - The ash shell is the most bash compatible and full featured one. + The ash shell is the most bash compatible and full featured one, + although compatibility is far from being complete. Note that selecting this option does not switch on any bash compatibility code. It merely makes it possible to install @@ -46,9 +57,19 @@ choice config BASH_IS_ASH depends on !NOMMU bool "ash" + help + Choose ash to be the shell executed by 'bash' name. + The ash code will be built into busybox. If you don't select + "ash" choice (CONFIG_ASH), this shell may only be invoked by + the name 'bash' (and not 'ash'). config BASH_IS_HUSH bool "hush" + help + Choose hush to be the shell executed by 'bash' name. + The hush code will be built into busybox. If you don't select + "hush" choice (CONFIG_HUSH), this shell may only be invoked by + the name 'bash' (and not 'hush'). config BASH_IS_NONE bool "none" -- cgit v1.2.3-55-g6feb From 9c083f599a9924ad04855ce94439598e75424ad9 Mon Sep 17 00:00:00 2001 From: Waldemar Brodkorb Date: Mon, 26 Dec 2016 20:07:59 +0100 Subject: modutils: remove special handling of uClibc Commit 3a45b87ac36f (modutils: support finit_module syscall) introduced macro finit_module. But it is not defined for uClibc. The compilation for busybox fails for MIPS with: With uClibc, we get following build errors: modutils/lib.a(modutils.o): In function `bb_init_module': modutils.c:(.text.bb_init_module+0x94): undefined reference to `finit_module' modutils.c:(.text.bb_init_module+0xa0): undefined reference to `finit_module' We can just use syscall() without any need for the uClibc wrappers. Newer versions of uClibc-ng (>1.0.20) will remove the module syscall wrappers. Found via Buildroot autobuilders: http://autobuild.buildroot.net/results/556/55655daef23788fb3967f801ec8b79e9bed7122b/build-end.log function old new delta bb_delete_module 26 32 +6 bb_init_module 90 95 +5 delete_module 37 - -37 init_module 53 - -53 ------------------------------------------------------------------------------ (add/remove: 0/4 grow/shrink: 2/0 up/down: 11/-90) Total: -79 bytes Reported-by: Rahul Bedarkar Signed-off-by: Waldemar Brodkorb Signed-off-by: Denys Vlasenko --- modutils/modprobe-small.c | 4 ++-- modutils/modutils.c | 16 ++++++---------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c index 652ff4dfa..0fc9ea454 100644 --- a/modutils/modprobe-small.c +++ b/modutils/modprobe-small.c @@ -39,8 +39,8 @@ #include #include -extern int init_module(void *module, unsigned long len, const char *options); -extern int delete_module(const char *module, unsigned flags); +#define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts) +#define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags) #ifdef __NR_finit_module # define finit_module(fd, uargs, flags) syscall(__NR_finit_module, fd, uargs, flags) #endif diff --git a/modutils/modutils.c b/modutils/modutils.c index d36caaf68..4204f06fe 100644 --- a/modutils/modutils.c +++ b/modutils/modutils.c @@ -7,17 +7,13 @@ */ #include "modutils.h" -#ifdef __UCLIBC__ -extern int init_module(void *module, unsigned long len, const char *options); -extern int delete_module(const char *module, unsigned int flags); -#else -# include -# define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts) -# if defined(__NR_finit_module) -# define finit_module(fd, uargs, flags) syscall(__NR_finit_module, fd, uargs, flags) -# endif -# define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags) +#include + +#define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts) +#if defined(__NR_finit_module) +# define finit_module(fd, uargs, flags) syscall(__NR_finit_module, fd, uargs, flags) #endif +#define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags) static module_entry *helper_get_module(module_db *db, const char *module, int create) { -- cgit v1.2.3-55-g6feb From e3c4db8b396e955ca01648ba51c0fd093f3cab56 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 5 Jan 2017 11:43:53 +0100 Subject: unzip: properly use CDF to find compressed files. Closes 9536 function old new delta unzip_main 2437 2350 -87 Signed-off-by: Denys Vlasenko --- archival/unzip.c | 285 +++++++++++++++++++++++++++++--------------------- testsuite/unzip.tests | 6 +- 2 files changed, 168 insertions(+), 123 deletions(-) diff --git a/archival/unzip.c b/archival/unzip.c index c540485ac..edef22f75 100644 --- a/archival/unzip.c +++ b/archival/unzip.c @@ -16,7 +16,6 @@ * TODO * Zip64 + other methods */ - //config:config UNZIP //config: bool "unzip" //config: default y @@ -24,8 +23,17 @@ //config: unzip will list or extract files from a ZIP archive, //config: commonly found on DOS/WIN systems. The default behavior //config: (with no options) is to extract the archive into the -//config: current directory. Use the `-d' option to extract to a -//config: directory of your choice. +//config: current directory. +//config: +//config:config FEATURE_UNZIP_CDF +//config: bool "Read and use Central Directory data" +//config: default y +//config: depends on UNZIP +//config: help +//config: If you know that you only need to deal with simple +//config: ZIP files without deleted/updated files, SFX archves etc, +//config: you can reduce code size by unselecting this option. +//config: To support less trivial ZIPs, say Y. //applet:IF_UNZIP(APPLET(unzip, BB_DIR_USR_BIN, BB_SUID_DROP)) //kbuild:lib-$(CONFIG_UNZIP) += unzip.o @@ -80,30 +88,20 @@ typedef union { uint32_t ucmpsize PACKED; /* 18-21 */ uint16_t filename_len; /* 22-23 */ uint16_t extra_len; /* 24-25 */ + /* filename follows (not NUL terminated) */ + /* extra field follows */ + /* data follows */ } formatted PACKED; } zip_header_t; /* PACKED - gcc 4.2.1 doesn't like it (spews warning) */ -/* Check the offset of the last element, not the length. This leniency - * allows for poor packing, whereby the overall struct may be too long, - * even though the elements are all in the right place. - */ -struct BUG_zip_header_must_be_26_bytes { - char BUG_zip_header_must_be_26_bytes[ - offsetof(zip_header_t, formatted.extra_len) + 2 - == ZIP_HEADER_LEN ? 1 : -1]; -}; - -#define FIX_ENDIANNESS_ZIP(zip_header) do { \ - (zip_header).formatted.version = SWAP_LE16((zip_header).formatted.version ); \ - (zip_header).formatted.method = SWAP_LE16((zip_header).formatted.method ); \ - (zip_header).formatted.modtime = SWAP_LE16((zip_header).formatted.modtime ); \ - (zip_header).formatted.moddate = SWAP_LE16((zip_header).formatted.moddate ); \ +#define FIX_ENDIANNESS_ZIP(zip_header) \ +do { if (BB_BIG_ENDIAN) { \ (zip_header).formatted.crc32 = SWAP_LE32((zip_header).formatted.crc32 ); \ (zip_header).formatted.cmpsize = SWAP_LE32((zip_header).formatted.cmpsize ); \ (zip_header).formatted.ucmpsize = SWAP_LE32((zip_header).formatted.ucmpsize ); \ (zip_header).formatted.filename_len = SWAP_LE16((zip_header).formatted.filename_len); \ (zip_header).formatted.extra_len = SWAP_LE16((zip_header).formatted.extra_len ); \ -} while (0) +}} while (0) #define CDF_HEADER_LEN 42 @@ -115,8 +113,8 @@ typedef union { uint16_t version_needed; /* 2-3 */ uint16_t cdf_flags; /* 4-5 */ uint16_t method; /* 6-7 */ - uint16_t mtime; /* 8-9 */ - uint16_t mdate; /* 10-11 */ + uint16_t modtime; /* 8-9 */ + uint16_t moddate; /* 10-11 */ uint32_t crc32; /* 12-15 */ uint32_t cmpsize; /* 16-19 */ uint32_t ucmpsize; /* 20-23 */ @@ -127,27 +125,27 @@ typedef union { uint16_t internal_file_attributes; /* 32-33 */ uint32_t external_file_attributes PACKED; /* 34-37 */ uint32_t relative_offset_of_local_header PACKED; /* 38-41 */ + /* filename follows (not NUL terminated) */ + /* extra field follows */ + /* comment follows */ } formatted PACKED; } cdf_header_t; -struct BUG_cdf_header_must_be_42_bytes { - char BUG_cdf_header_must_be_42_bytes[ - offsetof(cdf_header_t, formatted.relative_offset_of_local_header) + 4 - == CDF_HEADER_LEN ? 1 : -1]; -}; - -#define FIX_ENDIANNESS_CDF(cdf_header) do { \ +#define FIX_ENDIANNESS_CDF(cdf_header) \ +do { if (BB_BIG_ENDIAN) { \ + (cdf_header).formatted.version_made_by = SWAP_LE16((cdf_header).formatted.version_made_by); \ + (cdf_header).formatted.version_needed = SWAP_LE16((cdf_header).formatted.version_needed); \ + (cdf_header).formatted.method = SWAP_LE16((cdf_header).formatted.method ); \ + (cdf_header).formatted.modtime = SWAP_LE16((cdf_header).formatted.modtime ); \ + (cdf_header).formatted.moddate = SWAP_LE16((cdf_header).formatted.moddate ); \ (cdf_header).formatted.crc32 = SWAP_LE32((cdf_header).formatted.crc32 ); \ (cdf_header).formatted.cmpsize = SWAP_LE32((cdf_header).formatted.cmpsize ); \ (cdf_header).formatted.ucmpsize = SWAP_LE32((cdf_header).formatted.ucmpsize ); \ (cdf_header).formatted.file_name_length = SWAP_LE16((cdf_header).formatted.file_name_length); \ (cdf_header).formatted.extra_field_length = SWAP_LE16((cdf_header).formatted.extra_field_length); \ (cdf_header).formatted.file_comment_length = SWAP_LE16((cdf_header).formatted.file_comment_length); \ - IF_DESKTOP( \ - (cdf_header).formatted.version_made_by = SWAP_LE16((cdf_header).formatted.version_made_by); \ (cdf_header).formatted.external_file_attributes = SWAP_LE32((cdf_header).formatted.external_file_attributes); \ - ) \ -} while (0) +}} while (0) #define CDE_HEADER_LEN 16 @@ -166,20 +164,38 @@ typedef union { } formatted PACKED; } cde_header_t; -struct BUG_cde_header_must_be_16_bytes { +#define FIX_ENDIANNESS_CDE(cde_header) \ +do { if (BB_BIG_ENDIAN) { \ + (cde_header).formatted.cdf_offset = SWAP_LE32((cde_header).formatted.cdf_offset); \ +}} while (0) + +struct BUG { + /* Check the offset of the last element, not the length. This leniency + * allows for poor packing, whereby the overall struct may be too long, + * even though the elements are all in the right place. + */ + char BUG_zip_header_must_be_26_bytes[ + offsetof(zip_header_t, formatted.extra_len) + 2 + == ZIP_HEADER_LEN ? 1 : -1]; + char BUG_cdf_header_must_be_42_bytes[ + offsetof(cdf_header_t, formatted.relative_offset_of_local_header) + 4 + == CDF_HEADER_LEN ? 1 : -1]; char BUG_cde_header_must_be_16_bytes[ sizeof(cde_header_t) == CDE_HEADER_LEN ? 1 : -1]; }; -#define FIX_ENDIANNESS_CDE(cde_header) do { \ - (cde_header).formatted.cdf_offset = SWAP_LE32((cde_header).formatted.cdf_offset); \ -} while (0) enum { zip_fd = 3 }; -#if ENABLE_DESKTOP +/* This value means that we failed to find CDF */ +#define BAD_CDF_OFFSET ((uint32_t)0xffffffff) + +#if !ENABLE_FEATURE_UNZIP_CDF +# define find_cdf_offset() BAD_CDF_OFFSET + +#else /* Seen in the wild: * Self-extracting PRO2K3XP_32.exe contains 19078464 byte zip archive, * where CDE was nearly 48 kbytes before EOF. @@ -188,25 +204,26 @@ enum { zip_fd = 3 }; * To make extraction work, bumped PEEK_FROM_END from 16k to 64k. */ #define PEEK_FROM_END (64*1024) - -/* This value means that we failed to find CDF */ -#define BAD_CDF_OFFSET ((uint32_t)0xffffffff) - /* NB: does not preserve file position! */ static uint32_t find_cdf_offset(void) { cde_header_t cde_header; + unsigned char *buf; unsigned char *p; off_t end; - unsigned char *buf = xzalloc(PEEK_FROM_END); uint32_t found; - end = xlseek(zip_fd, 0, SEEK_END); + end = lseek(zip_fd, 0, SEEK_END); + if (end == (off_t) -1) + return BAD_CDF_OFFSET; + end -= PEEK_FROM_END; if (end < 0) end = 0; + dbg("Looking for cdf_offset starting from 0x%"OFF_FMT"x", end); xlseek(zip_fd, end, SEEK_SET); + buf = xzalloc(PEEK_FROM_END); full_read(zip_fd, buf, PEEK_FROM_END); found = BAD_CDF_OFFSET; @@ -252,30 +269,36 @@ static uint32_t find_cdf_offset(void) static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr) { off_t org; + uint32_t magic; - org = xlseek(zip_fd, 0, SEEK_CUR); + if (cdf_offset == BAD_CDF_OFFSET) + return cdf_offset; - if (!cdf_offset) - cdf_offset = find_cdf_offset(); - - if (cdf_offset != BAD_CDF_OFFSET) { - dbg("Reading CDF at 0x%x", (unsigned)cdf_offset); - xlseek(zip_fd, cdf_offset + 4, SEEK_SET); - xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN); - FIX_ENDIANNESS_CDF(*cdf_ptr); - dbg(" file_name_length:%u extra_field_length:%u file_comment_length:%u", - (unsigned)cdf_ptr->formatted.file_name_length, - (unsigned)cdf_ptr->formatted.extra_field_length, - (unsigned)cdf_ptr->formatted.file_comment_length - ); - cdf_offset += 4 + CDF_HEADER_LEN - + cdf_ptr->formatted.file_name_length - + cdf_ptr->formatted.extra_field_length - + cdf_ptr->formatted.file_comment_length; + org = xlseek(zip_fd, 0, SEEK_CUR); + dbg("Reading CDF at 0x%x", (unsigned)cdf_offset); + xlseek(zip_fd, cdf_offset, SEEK_SET); + xread(zip_fd, &magic, 4); + /* Central Directory End? */ + if (magic == ZIP_CDE_MAGIC) { + dbg("got ZIP_CDE_MAGIC"); + return 0; /* EOF */ } + xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN); + /* Caller doesn't need this: */ + /* dbg("Returning file position to 0x%"OFF_FMT"x", org); */ + /* xlseek(zip_fd, org, SEEK_SET); */ + + FIX_ENDIANNESS_CDF(*cdf_ptr); + dbg(" file_name_length:%u extra_field_length:%u file_comment_length:%u", + (unsigned)cdf_ptr->formatted.file_name_length, + (unsigned)cdf_ptr->formatted.extra_field_length, + (unsigned)cdf_ptr->formatted.file_comment_length + ); + cdf_offset += 4 + CDF_HEADER_LEN + + cdf_ptr->formatted.file_name_length + + cdf_ptr->formatted.extra_field_length + + cdf_ptr->formatted.file_comment_length; - dbg("Returning file position to 0x%"OFF_FMT"x", org); - xlseek(zip_fd, org, SEEK_SET); return cdf_offset; }; #endif @@ -324,6 +347,7 @@ static void unzip_extract(zip_header_t *zip_header, int dst_fd) bb_error_msg("bad length"); } } + /* TODO? method 12: bzip2, method 14: LZMA */ } static void my_fgets80(char *buf80) @@ -339,15 +363,12 @@ int unzip_main(int argc, char **argv) { enum { O_PROMPT, O_NEVER, O_ALWAYS }; - zip_header_t zip_header; smallint quiet = 0; - IF_NOT_DESKTOP(const) smallint verbose = 0; + IF_NOT_FEATURE_UNZIP_CDF(const) smallint verbose = 0; smallint listing = 0; smallint overwrite = O_PROMPT; smallint x_opt_seen; -#if ENABLE_DESKTOP uint32_t cdf_offset; -#endif unsigned long total_usize; unsigned long total_size; unsigned total_entries; @@ -430,7 +451,7 @@ int unzip_main(int argc, char **argv) break; case 'v': /* Verbose list */ - IF_DESKTOP(verbose++;) + IF_FEATURE_UNZIP_CDF(verbose++;) listing = 1; break; @@ -545,78 +566,102 @@ int unzip_main(int argc, char **argv) total_usize = 0; total_size = 0; total_entries = 0; -#if ENABLE_DESKTOP - cdf_offset = 0; -#endif + cdf_offset = find_cdf_offset(); /* try to seek to the end, find CDE and CDF start */ while (1) { - uint32_t magic; + zip_header_t zip_header; mode_t dir_mode = 0777; -#if ENABLE_DESKTOP +#if ENABLE_FEATURE_UNZIP_CDF mode_t file_mode = 0666; #endif - /* Check magic number */ - xread(zip_fd, &magic, 4); - /* Central directory? It's at the end, so exit */ - if (magic == ZIP_CDF_MAGIC) { - dbg("got ZIP_CDF_MAGIC"); - break; - } -#if ENABLE_DESKTOP - /* Data descriptor? It was a streaming file, go on */ - if (magic == ZIP_DD_MAGIC) { - dbg("got ZIP_DD_MAGIC"); - /* skip over duplicate crc32, cmpsize and ucmpsize */ - unzip_skip(3 * 4); - continue; - } -#endif - if (magic != ZIP_FILEHEADER_MAGIC) - bb_error_msg_and_die("invalid zip magic %08X", (int)magic); - dbg("got ZIP_FILEHEADER_MAGIC"); - - /* Read the file header */ - xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN); - FIX_ENDIANNESS_ZIP(zip_header); - if ((zip_header.formatted.method != 0) && (zip_header.formatted.method != 8)) { - bb_error_msg_and_die("unsupported method %d", zip_header.formatted.method); - } -#if !ENABLE_DESKTOP - if (zip_header.formatted.zip_flags & SWAP_LE16(0x0009)) { - bb_error_msg_and_die("zip flags 1 and 8 are not supported"); - } -#else - if (zip_header.formatted.zip_flags & SWAP_LE16(0x0001)) { - /* 0x0001 - encrypted */ - bb_error_msg_and_die("zip flag 1 (encryption) is not supported"); - } + if (!ENABLE_FEATURE_UNZIP_CDF || cdf_offset == BAD_CDF_OFFSET) { + /* Normally happens when input is unseekable. + * + * Valid ZIP file has Central Directory at the end + * with central directory file headers (CDFs). + * After it, there is a Central Directory End structure. + * CDFs identify what files are in the ZIP and where + * they are located. This allows ZIP readers to load + * the list of files without reading the entire ZIP archive. + * ZIP files may be appended to, only files specified in + * the CD are valid. Scanning for local file headers is + * not a correct algorithm. + * + * We try to do the above, and resort to "linear" reading + * of ZIP file only if seek failed or CDE wasn't found. + */ + uint32_t magic; - if (cdf_offset != BAD_CDF_OFFSET) { + /* Check magic number */ + xread(zip_fd, &magic, 4); + /* Central directory? It's at the end, so exit */ + if (magic == ZIP_CDF_MAGIC) { + dbg("got ZIP_CDF_MAGIC"); + break; + } + /* Data descriptor? It was a streaming file, go on */ + if (magic == ZIP_DD_MAGIC) { + dbg("got ZIP_DD_MAGIC"); + /* skip over duplicate crc32, cmpsize and ucmpsize */ + unzip_skip(3 * 4); + continue; + } + if (magic != ZIP_FILEHEADER_MAGIC) + bb_error_msg_and_die("invalid zip magic %08X", (int)magic); + dbg("got ZIP_FILEHEADER_MAGIC"); + + xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN); + FIX_ENDIANNESS_ZIP(zip_header); + if ((zip_header.formatted.method != 0) + && (zip_header.formatted.method != 8) + ) { + /* TODO? method 12: bzip2, method 14: LZMA */ + bb_error_msg_and_die("unsupported method %d", zip_header.formatted.method); + } + if (zip_header.formatted.zip_flags & SWAP_LE16(0x0009)) { + bb_error_msg_and_die("zip flags 1 and 8 are not supported"); + } + } +#if ENABLE_FEATURE_UNZIP_CDF + else { + /* cdf_offset is valid (and we know the file is seekable) */ cdf_header_t cdf_header; cdf_offset = read_next_cdf(cdf_offset, &cdf_header); - /* - * Note: cdf_offset can become BAD_CDF_OFFSET after the above call. - */ + if (cdf_offset == 0) /* EOF? */ + break; +# if 0 + xlseek(zip_fd, + SWAP_LE32(cdf_header.formatted.relative_offset_of_local_header) + 4, + SEEK_SET); + xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN); + FIX_ENDIANNESS_ZIP(zip_header); if (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) { /* 0x0008 - streaming. [u]cmpsize can be reliably gotten - * only from Central Directory. See unzip_doc.txt + * only from Central Directory. */ zip_header.formatted.crc32 = cdf_header.formatted.crc32; zip_header.formatted.cmpsize = cdf_header.formatted.cmpsize; zip_header.formatted.ucmpsize = cdf_header.formatted.ucmpsize; } +# else + /* CDF has the same data as local header, no need to read the latter */ + memcpy(&zip_header.formatted.version, + &cdf_header.formatted.version_needed, ZIP_HEADER_LEN); + xlseek(zip_fd, + SWAP_LE32(cdf_header.formatted.relative_offset_of_local_header) + 4 + ZIP_HEADER_LEN, + SEEK_SET); +# endif if ((cdf_header.formatted.version_made_by >> 8) == 3) { /* This archive is created on Unix */ dir_mode = file_mode = (cdf_header.formatted.external_file_attributes >> 16); } } - if (cdf_offset == BAD_CDF_OFFSET - && (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) - ) { - /* If it's a streaming zip, we _require_ CDF */ - bb_error_msg_and_die("can't find file table"); - } #endif + + if (zip_header.formatted.zip_flags & SWAP_LE16(0x0001)) { + /* 0x0001 - encrypted */ + bb_error_msg_and_die("zip flag 1 (encryption) is not supported"); + } dbg("File cmpsize:0x%x extra_len:0x%x ucmpsize:0x%x", (unsigned)zip_header.formatted.cmpsize, (unsigned)zip_header.formatted.extra_len, @@ -751,7 +796,7 @@ int unzip_main(int argc, char **argv) overwrite = O_ALWAYS; case 'y': /* Open file and fall into unzip */ unzip_create_leading_dirs(dst_fn); -#if ENABLE_DESKTOP +#if ENABLE_FEATURE_UNZIP_CDF dst_fd = xopen3(dst_fn, O_WRONLY | O_CREAT | O_TRUNC, file_mode); #else dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC); diff --git a/testsuite/unzip.tests b/testsuite/unzip.tests index d8738a3bd..d9c45242c 100755 --- a/testsuite/unzip.tests +++ b/testsuite/unzip.tests @@ -31,11 +31,10 @@ rmdir foo rm foo.zip # File containing some damaged encrypted stream +optional FEATURE_UNZIP_CDF testing "unzip (bad archive)" "uudecode; unzip bad.zip 2>&1; echo \$?" \ "Archive: bad.zip - inflating: ]3j½r«IK-%Ix -unzip: corrupted data -unzip: inflate error +unzip: short read 1 " \ "" "\ @@ -49,6 +48,7 @@ BDYAAAAMAAEADQAAADIADQAAAEEAAAASw73Ct1DKokohPXQiNzA+FAI1HCcW NzITNFBLBQUKAC4JAA04Cw0EOhZQSwUGAQAABAIAAgCZAAAAeQAAAAIALhM= ==== " +SKIP= rm * -- cgit v1.2.3-55-g6feb From ecba2944d576c053ec65a4cf5d8a084137cb69f8 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 5 Jan 2017 11:47:28 +0100 Subject: typo fix in config help text Signed-off-by: Denys Vlasenko --- archival/unzip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archival/unzip.c b/archival/unzip.c index edef22f75..f1726439d 100644 --- a/archival/unzip.c +++ b/archival/unzip.c @@ -31,7 +31,7 @@ //config: depends on UNZIP //config: help //config: If you know that you only need to deal with simple -//config: ZIP files without deleted/updated files, SFX archves etc, +//config: ZIP files without deleted/updated files, SFX archives etc, //config: you can reduce code size by unselecting this option. //config: To support less trivial ZIPs, say Y. -- cgit v1.2.3-55-g6feb From e6add210b220bd8fe56ca711279b44e111bdd8f0 Mon Sep 17 00:00:00 2001 From: Cristian Ionescu-Idbohrn Date: Thu, 5 Jan 2017 19:07:54 +0100 Subject: unzip: remove now-pointless lseek which returns current position archival/unzip.c: In function 'read_next_cdf': archival/unzip.c:271:8: warning: variable 'org' set but not used [-Wunused-but-set-variable] off_t org; ^~~ Signed-off-by: Cristian Ionescu-Idbohrn Signed-off-by: Denys Vlasenko --- archival/unzip.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/archival/unzip.c b/archival/unzip.c index f1726439d..98a71c09d 100644 --- a/archival/unzip.c +++ b/archival/unzip.c @@ -268,13 +268,11 @@ static uint32_t find_cdf_offset(void) static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr) { - off_t org; uint32_t magic; if (cdf_offset == BAD_CDF_OFFSET) return cdf_offset; - org = xlseek(zip_fd, 0, SEEK_CUR); dbg("Reading CDF at 0x%x", (unsigned)cdf_offset); xlseek(zip_fd, cdf_offset, SEEK_SET); xread(zip_fd, &magic, 4); @@ -284,9 +282,6 @@ static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr) return 0; /* EOF */ } xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN); - /* Caller doesn't need this: */ - /* dbg("Returning file position to 0x%"OFF_FMT"x", org); */ - /* xlseek(zip_fd, org, SEEK_SET); */ FIX_ENDIANNESS_CDF(*cdf_ptr); dbg(" file_name_length:%u extra_field_length:%u file_comment_length:%u", -- cgit v1.2.3-55-g6feb From b62ea34afed7d3bf60a6c8ef5a030fea52f55b10 Mon Sep 17 00:00:00 2001 From: Natanael Copa Date: Fri, 6 Jan 2017 16:18:45 +0100 Subject: ntpd: improve postponed hostname resolution Run the namelookup from the main loop so a misspelled first ntp server name does not block everything forever. This fixes the following situation which would block forever: $ sudo ./busybox ntpd -dn -p foobar -p pool.ntp.org ntpd: bad address 'foobar' ntpd: bad address 'foobar' ntpd: bad address 'foobar' ... New behavior: ntpd: bad address 'foobar' ntpd: sending query to 137.190.2.4 ntpd: reply from 137.190.2.4: offset:-1.009775 delay:0.175550 status:0x24 strat:1 refid:0x00535047 rootdelay:0.000000 reach:0x01 ntpd: sending query to 137.190.2.4 ntpd: reply from 137.190.2.4: offset:-1.009605 delay:0.175461 status:0x24 strat:1 refid:0x00535047 rootdelay:0.000000 reach:0x03 ntpd: sending query to 137.190.2.4 ntpd: reply from 137.190.2.4: offset:-1.005327 delay:0.167027 status:0x24 strat:1 refid:0x00535047 rootdelay:0.000000 reach:0x07 ntpd: sending query to 137.190.2.4 ntpd: bad address 'foobar' ntpd: reply from 137.190.2.4: offset:-1.046349 delay:0.248705 status:0x24 strat:1 refid:0x00535047 rootdelay:0.000000 reach:0x0f This patch is based on Kaarle Ritvanens work. http://lists.busybox.net/pipermail/busybox/2016-May/084197.html function old new delta ntpd_main 1061 1079 +18 ntp_init 556 560 +4 resolve_peer_hostname 81 75 -6 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/1 up/down: 22/-6) Total: 16 bytes Signed-off-by: Kaarle Ritvanen Signed-off-by: Natanael Copa Signed-off-by: Denys Vlasenko --- networking/ntpd.c | 73 ++++++++++++++++++++++++++----------------------------- 1 file changed, 34 insertions(+), 39 deletions(-) diff --git a/networking/ntpd.c b/networking/ntpd.c index b7fa5dce9..bfd5705fc 100644 --- a/networking/ntpd.c +++ b/networking/ntpd.c @@ -155,6 +155,7 @@ #define RETRY_INTERVAL 32 /* on send/recv error, retry in N secs (need to be power of 2) */ #define NOREPLY_INTERVAL 512 /* sent, but got no reply: cap next query by this many seconds */ #define RESPONSE_INTERVAL 16 /* wait for reply up to N secs */ +#define HOSTNAME_INTERVAL 5 /* hostname lookup failed. Wait N secs for next try */ /* Step threshold (sec). std ntpd uses 0.128. */ @@ -790,28 +791,20 @@ reset_peer_stats(peer_t *p, double offset) VERB6 bb_error_msg("%s->lastpkt_recv_time=%f", p->p_dotted, p->lastpkt_recv_time); } -static void -resolve_peer_hostname(peer_t *p, int loop_on_fail) +static len_and_sockaddr* +resolve_peer_hostname(peer_t *p) { - len_and_sockaddr *lsa; - - again: - lsa = host2sockaddr(p->p_hostname, 123); - if (!lsa) { - /* error message already emitted by host2sockaddr() */ - if (!loop_on_fail) - return; -//FIXME: do this to avoid infinite looping on typo in a hostname? -//well... in which case, what is a good value for loop_on_fail? - //if (--loop_on_fail == 0) - // xfunc_die(); - sleep(5); - goto again; + len_and_sockaddr *lsa = host2sockaddr(p->p_hostname, 123); + if (lsa) { + free(p->p_lsa); + free(p->p_dotted); + p->p_lsa = lsa; + p->p_dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa); + } else { + /* error message is emitted by host2sockaddr() */ + set_next(p, HOSTNAME_INTERVAL); } - free(p->p_lsa); - free(p->p_dotted); - p->p_lsa = lsa; - p->p_dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa); + return lsa; } static void @@ -822,28 +815,28 @@ add_peers(const char *s) p = xzalloc(sizeof(*p) + strlen(s)); strcpy(p->p_hostname, s); - resolve_peer_hostname(p, /*loop_on_fail=*/ 1); + p->p_fd = -1; + p->p_xmt_msg.m_status = MODE_CLIENT | (NTP_VERSION << 3); + p->next_action_time = G.cur_time; /* = set_next(p, 0); */ + reset_peer_stats(p, STEP_THRESHOLD); /* Names like N..pool.ntp.org are randomly resolved * to a pool of machines. Sometimes different N's resolve to the same IP. * It is not useful to have two peers with same IP. We skip duplicates. */ - for (item = G.ntp_peers; item != NULL; item = item->link) { - peer_t *pp = (peer_t *) item->data; - if (strcmp(p->p_dotted, pp->p_dotted) == 0) { - bb_error_msg("duplicate peer %s (%s)", s, p->p_dotted); - free(p->p_lsa); - free(p->p_dotted); - free(p); - return; + if (resolve_peer_hostname(p)) { + for (item = G.ntp_peers; item != NULL; item = item->link) { + peer_t *pp = (peer_t *) item->data; + if (pp->p_dotted && strcmp(p->p_dotted, pp->p_dotted) == 0) { + bb_error_msg("duplicate peer %s (%s)", s, p->p_dotted); + free(p->p_lsa); + free(p->p_dotted); + free(p); + return; + } } } - p->p_fd = -1; - p->p_xmt_msg.m_status = MODE_CLIENT | (NTP_VERSION << 3); - p->next_action_time = G.cur_time; /* = set_next(p, 0); */ - reset_peer_stats(p, STEP_THRESHOLD); - llist_add_to(&G.ntp_peers, p); G.peer_cnt++; } @@ -871,6 +864,11 @@ do_sendto(int fd, static void send_query_to_peer(peer_t *p) { + if (!p->p_lsa) { + if (!resolve_peer_hostname(p)) + return; + } + /* Why do we need to bind()? * See what happens when we don't bind: * @@ -2238,7 +2236,7 @@ static NOINLINE void ntp_init(char **argv) IF_FEATURE_NTPD_SERVER("I:") /* compat */ "d" /* compat */ "46aAbgL", /* compat, ignored */ - &peers,&G.script_name, + &peers, &G.script_name, #if ENABLE_FEATURE_NTPD_SERVER &G.if_name, #endif @@ -2263,9 +2261,6 @@ static NOINLINE void ntp_init(char **argv) if (opts & OPT_N) setpriority(PRIO_PROCESS, 0, -15); - /* add_peers() calls can retry DNS resolution (possibly forever). - * Daemonize before them, or else boot can stall forever. - */ if (!(opts & OPT_n)) { bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO, argv); logmode = LOGMODE_NONE; @@ -2400,7 +2395,7 @@ int ntpd_main(int argc UNUSED_PARAM, char **argv) /* What if don't see it because it changed its IP? */ if (p->reachable_bits == 0) - resolve_peer_hostname(p, /*loop_on_fail=*/ 0); + resolve_peer_hostname(p); set_next(p, timeout); } -- cgit v1.2.3-55-g6feb From 6cd0294725cf40a3ef0bb0a1dcc7a7044a85cbf5 Mon Sep 17 00:00:00 2001 From: Kang-Che Sung Date: Fri, 6 Jan 2017 17:02:03 +0100 Subject: ash: explicltly group ash options This would makes all ash options indented inside "ash" in menuconfig. It appears that menuconfig has a limit at tracking multiple dependency lines like this (it looks like a "diamond problem" but I'm not sure if it is): ---ASH <---------- / \ ASH_OPTIMIZE_FOR_SIZE !NOMMU <-*----SH_IS_ASH <----[OR] <--ASH_INTERNAL_GLOB \ / ASH_RANDOM_SUPPORT ---BASH_IS_ASH <-- [...] The kconfig-language document [1] states that: > If a menu entry somehow depends on the previous entry, it can be > made a submenu of it. First, the previous (parent) symbol must be > part of the dependency list and then one of these two conditions > must be true: > - the child entry must become invisible, if the parent is set to 'n' [BusyBox ash used to satisfy this, but no longer does] > - the child entry must only be visible, if the parent is visible [BusyBox ash configs actually satisfy this, but because of "diamond" above this might not be easily detected] So I found out a direct workaround: by making ash options explicitly depend on !NOMMU, we can tell menuconfig that rule 2 above is satisfied without any more tracking. --------------------- / \ !NOMMU <-*-----ASH <-------- \ \ \ \ ASH_OPTIMIZE_FOR_SIZE *---SH_IS_ASH <---[OR]-[AND] <--ASH_INTERNAL_GLOB \ / ASH_RANDOM_SUPPORT --BASH_IS_ASH <- [...] So all ash options would now be indented under "ash". [1] "Documentation/kbuild/kconfig-language.txt" in Linux kernel source Signed-off-by: Kang-Che Sung Signed-off-by: Denys Vlasenko --- shell/Config.src | 4 ++++ shell/ash.c | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/shell/Config.src b/shell/Config.src index 9bd493fed..3545f05dd 100644 --- a/shell/Config.src +++ b/shell/Config.src @@ -80,6 +80,9 @@ endchoice INSERT +comment "Options common to all shells" +if ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH + config FEATURE_SH_MATH bool "POSIX math support" default y @@ -163,5 +166,6 @@ config FEATURE_SH_HISTFILESIZE to set shell history size. Note that its max value is capped by "History size" setting in library tuning section. +endif # Options common to all shells endmenu diff --git a/shell/ash.c b/shell/ash.c index 7c53946ce..aee3d419c 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -26,6 +26,11 @@ //config: shell (by Herbert Xu), which was created by porting the 'ash' shell //config: (written by Kenneth Almquist) from NetBSD. //config: +//config:# ash options +//config:# note: Don't remove !NOMMU part in the next line; it would break +//config:# menuconfig's indenting. +//config:if !NOMMU && (ASH || SH_IS_ASH || BASH_IS_ASH) +//config: //config:config ASH_OPTIMIZE_FOR_SIZE //config: bool "Optimize for size instead of speed" //config: default y @@ -140,6 +145,8 @@ //config: depends on ASH || SH_IS_ASH || BASH_IS_ASH //config: help //config: Enable "check for new mail" function in the ash shell. +//config: +//config:endif # ash options //applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) //applet:IF_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, ash)) -- cgit v1.2.3-55-g6feb From fe93624fb69e3858ebaea2d9e53e0522c1025698 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 6 Jan 2017 19:16:36 +0100 Subject: Automatically disable FEATURE_COMPRESS_USAGE for small builds. Signed-off-by: Denys Vlasenko --- applets/usage_compressed | 3 +++ libbb/appletlib.c | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/applets/usage_compressed b/applets/usage_compressed index 186fcde77..36fc2a007 100755 --- a/applets/usage_compressed +++ b/applets/usage_compressed @@ -36,6 +36,9 @@ echo '' #0000040 114 105 135 040 133 055 141 040 101 103 124 111 117 116 106 111 # 042514 020135 026533 020141 041501 044524 047117 044506 +echo "#define UNPACKED_USAGE_LENGTH `$loc/usage | wc -c`" +echo + echo '#define PACKED_USAGE \' ## Breaks on big-endian systems! ## # Extra effort to avoid using "od -t x1": -t is not available diff --git a/libbb/appletlib.c b/libbb/appletlib.c index 805cd3ae6..bf6d4762c 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c @@ -52,6 +52,32 @@ #include "usage_compressed.h" + +/* "Do not compress usage text if uncompressed text is small + * and we don't include bunzip2 code for other reasons" + * + * Useful for mass one-applet rebuild (bunzip2 code is ~2.7k). + * + * Unlike BUNZIP2, if FEATURE_SEAMLESS_BZ2 is on, bunzip2 code is built but + * still may be unused if none of the selected applets calls open_zipped() + * or its friends; we test for (FEATURE_SEAMLESS_BZ2 && ) instead. + * For example, only if TAR and FEATURE_SEAMLESS_BZ2 are both selected, + * then bunzip2 code will be linked in anyway, and disabling help compression + * would be not optimal: + */ +#if UNPACKED_USAGE_LENGTH < 4*1024 \ + && !(ENABLE_FEATURE_SEAMLESS_BZ2 && ENABLE_TAR) \ + && !(ENABLE_FEATURE_SEAMLESS_BZ2 && ENABLE_MODPROBE) \ + && !(ENABLE_FEATURE_SEAMLESS_BZ2 && ENABLE_INSMOD) \ + && !(ENABLE_FEATURE_SEAMLESS_BZ2 && ENABLE_DEPMOD) \ + && !(ENABLE_FEATURE_SEAMLESS_BZ2 && ENABLE_MAN) \ + && !ENABLE_BUNZIP2 \ + && !ENABLE_BZCAT +# undef ENABLE_FEATURE_COMPRESS_USAGE +# define ENABLE_FEATURE_COMPRESS_USAGE 0 +#endif + + #if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE static const char usage_messages[] ALIGN1 = UNPACKED_USAGE; #else -- cgit v1.2.3-55-g6feb From 86584e134eec1a81298149f8c04c77727f6dccb9 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sat, 7 Jan 2017 10:15:01 +0100 Subject: ash: fix open fds leaking in redirects. Closes 9561 commit e19923f6652a638ac39c84012e97f52cf5a7568e deleted clearredir() call in shellexec(): ash: [REDIR] Remove redundant CLOEXEC calls Upstream commit: Now that we're marking file descriptors as CLOEXEC in savefd, we no longer need to close them on exec or in setinputfd. but it missed one place where we don't set CLOEXEC. Fixing this. Signed-off-by: Denys Vlasenko --- shell/ash.c | 11 +++++++---- shell/ash_test/ash-redir/redir_leak.right | 6 ++++++ shell/ash_test/ash-redir/redir_leak.tests | 10 ++++++++++ shell/hush_test/hush-redir/redir_leak.right | 6 ++++++ shell/hush_test/hush-redir/redir_leak.tests | 10 ++++++++++ 5 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 shell/ash_test/ash-redir/redir_leak.right create mode 100755 shell/ash_test/ash-redir/redir_leak.tests create mode 100644 shell/hush_test/hush-redir/redir_leak.right create mode 100755 shell/hush_test/hush-redir/redir_leak.tests diff --git a/shell/ash.c b/shell/ash.c index aee3d419c..efb4615db 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -5433,11 +5433,11 @@ redirect(union node *redir, int flags) /* Careful to not accidentally "save" * to the same fd as right side fd in N>&M */ int minfd = right_fd < 10 ? 10 : right_fd + 1; +#if defined(F_DUPFD_CLOEXEC) + i = fcntl(fd, F_DUPFD_CLOEXEC, minfd); +#else i = fcntl(fd, F_DUPFD, minfd); -/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds - * are closed in popredir() in the child, preventing them from leaking - * into child. (popredir() also cleans up the mess in case of failures) - */ +#endif if (i == -1) { i = errno; if (i != EBADF) { @@ -5452,6 +5452,9 @@ redirect(union node *redir, int flags) remember_to_close: i = CLOSED; } else { /* fd is open, save its copy */ +#if !defined(F_DUPFD_CLOEXEC) + fcntl(i, F_SETFD, FD_CLOEXEC); +#endif /* "exec fd>&-" should not close fds * which point to script file(s). * Force them to be restored afterwards */ diff --git a/shell/ash_test/ash-redir/redir_leak.right b/shell/ash_test/ash-redir/redir_leak.right new file mode 100644 index 000000000..b1c48292b --- /dev/null +++ b/shell/ash_test/ash-redir/redir_leak.right @@ -0,0 +1,6 @@ +4 +4 +4 +4 +4 +4 diff --git a/shell/ash_test/ash-redir/redir_leak.tests b/shell/ash_test/ash-redir/redir_leak.tests new file mode 100755 index 000000000..c8a9c6343 --- /dev/null +++ b/shell/ash_test/ash-redir/redir_leak.tests @@ -0,0 +1,10 @@ +# Each of these should show only four lines: +# fds 0,1,2 are stdio; fd 3 is open by opendir() in ls. +# This test detects bugs where redirects leave stray open fds. + +ls -1 /proc/self/fd | wc -l +ls -1 /proc/self/fd >/proc/self/fd/1 | wc -l +ls -1 /proc/self/fd >/proc/self/fd/1 2>&1 | wc -l +echo "`ls -1 /proc/self/fd `" | wc -l +echo "`ls -1 /proc/self/fd >/proc/self/fd/1 `" | wc -l +echo "`ls -1 /proc/self/fd >/proc/self/fd/1 2>&1 `" | wc -l diff --git a/shell/hush_test/hush-redir/redir_leak.right b/shell/hush_test/hush-redir/redir_leak.right new file mode 100644 index 000000000..b1c48292b --- /dev/null +++ b/shell/hush_test/hush-redir/redir_leak.right @@ -0,0 +1,6 @@ +4 +4 +4 +4 +4 +4 diff --git a/shell/hush_test/hush-redir/redir_leak.tests b/shell/hush_test/hush-redir/redir_leak.tests new file mode 100755 index 000000000..c8a9c6343 --- /dev/null +++ b/shell/hush_test/hush-redir/redir_leak.tests @@ -0,0 +1,10 @@ +# Each of these should show only four lines: +# fds 0,1,2 are stdio; fd 3 is open by opendir() in ls. +# This test detects bugs where redirects leave stray open fds. + +ls -1 /proc/self/fd | wc -l +ls -1 /proc/self/fd >/proc/self/fd/1 | wc -l +ls -1 /proc/self/fd >/proc/self/fd/1 2>&1 | wc -l +echo "`ls -1 /proc/self/fd `" | wc -l +echo "`ls -1 /proc/self/fd >/proc/self/fd/1 `" | wc -l +echo "`ls -1 /proc/self/fd >/proc/self/fd/1 2>&1 `" | wc -l -- cgit v1.2.3-55-g6feb