Apply post-1.23.0 patches, bump version to 1.23.1

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/Makefile b/Makefile
index b487f04..d449fa5 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 23
-SUBLEVEL = 0
+SUBLEVEL = 1
 EXTRAVERSION =
 NAME = Unnamed
 
diff --git a/editors/vi.c b/editors/vi.c
index 70bdbab..7b88e8e 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -542,9 +542,6 @@
 static int mysleep(int);
 static int readit(void);	// read (maybe cursor) key from stdin
 static int get_one_char(void);	// read 1 char from stdin
-#if !ENABLE_FEATURE_VI_READONLY
-#define file_insert(fn, p, update_ro_status) file_insert(fn, p)
-#endif
 // file_insert might reallocate text[]!
 static int file_insert(const char *, char *, int);
 static int file_write(char *, char *, char *);
@@ -1325,7 +1322,7 @@
 			q = next_line(q);
 		{ // dance around potentially-reallocated text[]
 			uintptr_t ofs = q - text;
-			size = file_insert(fn, q, /*update_ro:*/ 0);
+			size = file_insert(fn, q, 0);
 			q = text + ofs;
 		}
 		if (size < 0)
@@ -2905,7 +2902,7 @@
 }
 
 // might reallocate text[]!
-static int file_insert(const char *fn, char *p, int update_ro_status)
+static int file_insert(const char *fn, char *p, int initial)
 {
 	int cnt = -1;
 	int fd, size;
@@ -2918,7 +2915,8 @@
 
 	fd = open(fn, O_RDONLY);
 	if (fd < 0) {
-		status_line_bold_errno(fn);
+		if (!initial)
+			status_line_bold_errno(fn);
 		return cnt;
 	}
 
@@ -2946,7 +2944,7 @@
 	close(fd);
 
 #if ENABLE_FEATURE_VI_READONLY
-	if (update_ro_status
+	if (initial
 	 && ((access(fn, W_OK) < 0) ||
 		/* root will always have access()
 		 * so we check fileperms too */
diff --git a/modutils/depmod.c b/modutils/depmod.c
index aa228ec..a41b3e4 100644
--- a/modutils/depmod.c
+++ b/modutils/depmod.c
@@ -51,7 +51,11 @@
 
 	info->dnext = info->dprev = info;
 	info->name = xstrdup(fname + 2); /* skip "./" */
-	info->modname = xstrdup(filename2modname(fname, modname));
+	info->modname = xstrdup(
+		filename2modname(
+			bb_get_last_path_component_nostrip(fname),
+			modname
+	));
 	for (ptr = image; ptr < image + len - 10; ptr++) {
 		if (strncmp(ptr, "depends=", 8) == 0) {
 			char *u;
@@ -242,17 +246,18 @@
 	if (!(option_mask32 & OPT_n))
 		xfreopen_write("modules.alias", stdout);
 	for (m = modules; m != NULL; m = m->next) {
+		char modname[MODULE_NAME_LEN];
 		const char *fname = bb_basename(m->name);
-		int fnlen = strchrnul(fname, '.') - fname;
+		filename2modname(fname, modname);
 		while (m->aliases) {
 			/* Last word can well be m->modname instead,
 			 * but depmod from module-init-tools 3.4
 			 * uses module basename, i.e., no s/-/_/g.
 			 * (pathname and .ko.* are still stripped)
 			 * Mimicking that... */
-			printf("alias %s %.*s\n",
+			printf("alias %s %s\n",
 				(char*)llist_pop(&m->aliases),
-				fnlen, fname);
+				modname);
 		}
 	}
 #endif
@@ -260,12 +265,13 @@
 	if (!(option_mask32 & OPT_n))
 		xfreopen_write("modules.symbols", stdout);
 	for (m = modules; m != NULL; m = m->next) {
+		char modname[MODULE_NAME_LEN];
 		const char *fname = bb_basename(m->name);
-		int fnlen = strchrnul(fname, '.') - fname;
+		filename2modname(fname, modname);
 		while (m->symbols) {
-			printf("alias symbol:%s %.*s\n",
+			printf("alias symbol:%s %s\n",
 				(char*)llist_pop(&m->symbols),
-				fnlen, fname);
+				modname);
 		}
 	}
 #endif
diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c
index b7990bf..cf8a3f0 100644
--- a/modutils/modprobe-small.c
+++ b/modutils/modprobe-small.c
@@ -149,9 +149,13 @@
 static char *filename2modname(const char *filename, char *modname)
 {
 	int i;
-	char *from;
+	const char *from;
 
-	from = bb_get_last_path_component_nostrip(filename);
+	// Disabled since otherwise "modprobe dir/name" would work
+	// as if it is "modprobe name". It is unclear why
+	// 'basenamization' was here in the first place.
+	//from = bb_get_last_path_component_nostrip(filename);
+	from = filename;
 	for (i = 0; i < (MODULE_NAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++)
 		modname[i] = (from[i] == '-') ? '_' : from[i];
 	modname[i] = '\0';
@@ -631,6 +635,14 @@
 		infovec = find_alias(name);
 	}
 
+	if (!infovec) {
+		/* both dirscan and find_alias found nothing */
+		if (!is_rmmod && applet_name[0] != 'd') /* it wasn't rmmod or depmod */
+			bb_error_msg("module '%s' not found", name);
+//TODO: _and_die()? or should we continue (un)loading modules listed on cmdline?
+		goto ret;
+	}
+
 	/* There can be more than one module for the given alias. For example,
 	 * "pci:v00008086d00007010sv00000000sd00000000bc01sc01i80" matches
 	 * ata_piix because it has alias "pci:v00008086d00007010sv*sd*bc*sc*i*"
@@ -646,7 +658,8 @@
 			int r;
 			char modname[MODULE_NAME_LEN];
 
-			filename2modname(info->pathname, modname);
+			filename2modname(
+				bb_get_last_path_component_nostrip(info->pathname), modname);
 			r = delete_module(modname, O_NONBLOCK | O_EXCL);
 			dbg1_error_msg("delete_module('%s', O_NONBLOCK | O_EXCL):%d", modname, r);
 			if (r != 0) {
@@ -669,14 +682,6 @@
 		 */
 	}
 
-	if (!infovec) {
-		/* both dirscan and find_alias found nothing */
-		if (!is_rmmod && applet_name[0] != 'd') /* it wasn't rmmod or depmod */
-			bb_error_msg("module '%s' not found", name);
-//TODO: _and_die()? or should we continue (un)loading modules listed on cmdline?
-		goto ret;
-	}
-
 	infoidx = 0;
 	while ((info = infovec[infoidx++]) != NULL) {
 		/* Iterate thru dependencies, trying to (un)load them */
diff --git a/modutils/modprobe.c b/modutils/modprobe.c
index f090428..aedde5c 100644
--- a/modutils/modprobe.c
+++ b/modutils/modprobe.c
@@ -229,26 +229,20 @@
 {
 	return helper_get_module(module, 1);
 }
-static ALWAYS_INLINE struct module_entry *get_modentry(const char *module)
+/* So far this function always gets a module pathname, never an alias name.
+ * The crucial difference is that pathname needs dirname stripping,
+ * while alias name must NOT do it!
+ * Testcase where dirname stripping is likely to go wrong: "modprobe devname:snd/timer"
+ */
+static ALWAYS_INLINE struct module_entry *get_modentry(const char *pathname)
 {
-	return helper_get_module(module, 0);
+	return helper_get_module(bb_get_last_path_component_nostrip(pathname), 0);
 }
 
 static void add_probe(const char *name)
 {
 	struct module_entry *m;
 
-	/*
-	 * get_or_add_modentry() strips path from name and works
-	 * on remaining basename.
-	 * This would make "rmmod dir/name" and "modprobe dir/name"
-	 * to work like "rmmod name" and "modprobe name",
-	 * which is wrong, and can be abused via implicit modprobing:
-	 * "ifconfig /usbserial up" tries to modprobe netdev-/usbserial.
-	 */
-	if (strchr(name, '/'))
-		bb_error_msg_and_die("malformed module name '%s'", name);
-
 	m = get_or_add_modentry(name);
 	if (!(option_mask32 & (OPT_REMOVE | OPT_SHOW_DEPS))
 	 && (m->flags & MODULE_FLAG_LOADED)
@@ -428,7 +422,7 @@
 
 		rc = 0;
 		fn = llist_pop(&m->deps); /* we leak it */
-		m2 = get_or_add_modentry(fn);
+		m2 = get_or_add_modentry(bb_get_last_path_component_nostrip(fn));
 
 		if (option_mask32 & OPT_REMOVE) {
 			/* modprobe -r */
@@ -510,7 +504,7 @@
 		colon = last_char_is(tokens[0], ':');
 		if (colon == NULL)
 			continue;
-		*colon = 0;
+		*colon = '\0';
 
 		m = get_modentry(tokens[0]);
 		if (m == NULL)
@@ -557,7 +551,6 @@
 
 	if (opt & OPT_LIST_ONLY) {
 		int i;
-		char name[MODULE_NAME_LEN];
 		char *colon, *tokens[2];
 		parser_t *p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read);
 
@@ -569,10 +562,14 @@
 			if (!colon)
 				continue;
 			*colon = '\0';
-			filename2modname(tokens[0], name);
 			if (!argv[0])
 				puts(tokens[0]);
 			else {
+				char name[MODULE_NAME_LEN];
+				filename2modname(
+					bb_get_last_path_component_nostrip(tokens[0]),
+					name
+				);
 				for (i = 0; argv[i]; i++) {
 					if (fnmatch(argv[i], name, 0) == 0) {
 						puts(tokens[0]);
diff --git a/modutils/modutils.c b/modutils/modutils.c
index 6187ca7..ff79d3f 100644
--- a/modutils/modutils.c
+++ b/modutils/modutils.c
@@ -48,13 +48,17 @@
 char* FAST_FUNC filename2modname(const char *filename, char *modname)
 {
 	int i;
-	char *from;
+	const char *from;
 
 	if (filename == NULL)
 		return NULL;
 	if (modname == NULL)
 		modname = xmalloc(MODULE_NAME_LEN);
-	from = bb_get_last_path_component_nostrip(filename);
+	// Disabled since otherwise "modprobe dir/name" would work
+	// as if it is "modprobe name". It is unclear why
+	// 'basenamization' was here in the first place.
+	//from = bb_get_last_path_component_nostrip(filename);
+	from = filename;
 	for (i = 0; i < (MODULE_NAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++)
 		modname[i] = (from[i] == '-') ? '_' : from[i];
 	modname[i] = '\0';
diff --git a/networking/ftpd.c b/networking/ftpd.c
index 6adcb1d..0c10e1f 100644
--- a/networking/ftpd.c
+++ b/networking/ftpd.c
@@ -1174,8 +1174,13 @@
 
 	//umask(077); - admin can set umask before starting us
 
-	/* Signals. We'll always take -EPIPE rather than a rude signal, thanks */
-	signal(SIGPIPE, SIG_IGN);
+	/* Signals */
+	bb_signals(0
+		/* We'll always take EPIPE rather than a rude signal, thanks */
+		+ (1 << SIGPIPE)
+		/* LIST command spawns chilren. Prevent zombies */
+		+ (1 << SIGCHLD)
+		, SIG_IGN);
 
 	/* Set up options on the command socket (do we need these all? why?) */
 	setsockopt(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY, &const_int_1, sizeof(const_int_1));
diff --git a/shell/ash.c b/shell/ash.c
index 90fb00f..c5ad969 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -6746,6 +6746,14 @@
 		len = strlen(p);
 		if (!(subtype == VSPLUS || subtype == VSLENGTH))
 			memtodest(p, len, syntax, quotes);
+#if ENABLE_UNICODE_SUPPORT
+		if (subtype == VSLENGTH && len > 0) {
+			reinit_unicode_for_ash();
+			if (unicode_status == UNICODE_ON) {
+				len = unicode_strlen(p);
+			}
+		}
+#endif
 		return len;
 	}
 
@@ -6829,15 +6837,7 @@
 		varunset(p, var, 0, 0);
 
 	if (subtype == VSLENGTH) {
-		ssize_t n = varlen;
-		if (n > 0) {
-			reinit_unicode_for_ash();
-			if (unicode_status == UNICODE_ON) {
-				const char *val = lookupvar(var);
-				n = unicode_strlen(val);
-			}
-		}
-		cvtnum(n > 0 ? n : 0);
+		cvtnum(varlen > 0 ? varlen : 0);
 		goto record;
 	}