Updates to handle Linux 2.4.0 kernels (kludged around the "none" entries in
/proc/mounts, added a hack to make sysinfo work with both old and new kernels).
 -Erik
diff --git a/Changelog b/Changelog
index 4417652..aa8b15d 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,13 @@
+0.46
+
+	* Updates to handle Linux 2.4.0 kernels (kludged around the
+	    "none" entries in /proc/mounts, added a hack to make sysinfo
+	    work with both old and new kernels).
+	* Fixed a nasty bug in tar when could mess up saved symlinks.
+
+	 -Erik Andersen
+
+
 0.45
 	* Now compiles vs libc5 (which can save lots of space for 
 	    embedded systems).
diff --git a/TODO b/TODO
index 1b456a6..a4bfa0c 100644
--- a/TODO
+++ b/TODO
@@ -39,31 +39,14 @@
 
 Linux 2.4.x kernels
 
-BusyBox 0.45 currently will not work with the Linux 2.4.x kernels.  
+BusyBox 0.46 currently will not work with the Linux 2.4.x kernels.  
 I know of the following problems:
 
-1) The sysinfo syscall has changed what it does (binary incompatable), breaking
-    init and free.
-2) BusyBox NFS support is broken with 2.4.x (needs to be adjusted for NFSv3 and
+1) BusyBox NFS support is broken with 2.4.x (needs to be adjusted for NFSv3 and
     kernel header changes).
-3) mount,umount,and df are all broken by the "none" entries for fake filesystems
-    such as the shared mem one.  Al Viro claims these will be disappearing soon...
-
-I made a kernel patch that reverts the sysinfo changes
-    http://kernelnotes.org/lnxlists/linux-kernel/lk_0006_01/msg00619.html
-
-and I have been fighting with Alan Cox to get these changes fixed in a binary
-compatable way, but Alan has so far not been very receptive.  I am planning on
-appealing to Linus (when he gets back from vacation) and then going with
-whatever he decides...
-
-So my thought is, 2.4.x just isn't ready for BusyBox to target it, and even if
-it was, BusyBox isn't ready yet either.  Seems to me like this will not be
-ready for a while, and we should just not worry about it yet.
 
 As long as I have BB_FEATURE_NFSMOUNT turned off, everything compiles cleanly
-for me with linux2.4.0test1-ac22-riel (i.e. I don't see the freeramdisk.c
-problem you reported).  I use Debian potato (gcc 2.95.2, GNU libc 2.1.3).
+for me with linux2.4.0test2.  I use Debian potato (gcc 2.95.2, GNU libc 2.1.3).
 Of course, as noted above, compiling != working.
 
 -----------
diff --git a/coreutils/df.c b/coreutils/df.c
index ba3227f..8d457ec 100644
--- a/coreutils/df.c
+++ b/coreutils/df.c
@@ -103,6 +103,9 @@
 		}
 
 		while ((mountEntry = getmntent(mountTable))) {
+			if (strcmp(mountEntry->mnt_fsname, "none") == 0) {
+				continue;
+			}
 			df(mountEntry->mnt_fsname, mountEntry->mnt_dir);
 		}
 		endmntent(mountTable);
diff --git a/df.c b/df.c
index ba3227f..8d457ec 100644
--- a/df.c
+++ b/df.c
@@ -103,6 +103,9 @@
 		}
 
 		while ((mountEntry = getmntent(mountTable))) {
+			if (strcmp(mountEntry->mnt_fsname, "none") == 0) {
+				continue;
+			}
 			df(mountEntry->mnt_fsname, mountEntry->mnt_dir);
 		}
 		endmntent(mountTable);
diff --git a/free.c b/free.c
index a81189b..997430b 100644
--- a/free.c
+++ b/free.c
@@ -23,20 +23,36 @@
 
 #include "internal.h"
 #include <stdio.h>
-#include <sys/sysinfo.h>
+#include <errno.h>
 
-#define DIVISOR	1024
+
 extern int free_main(int argc, char **argv)
 {
 	struct sysinfo info;
 	sysinfo(&info);
-	info.totalram/=DIVISOR;
-	info.freeram/=DIVISOR;
-	info.totalswap/=DIVISOR;
-	info.freeswap/=DIVISOR;
-	info.sharedram/=DIVISOR;
-	info.bufferram/=DIVISOR;
-
+	/* Kernels prior to 2.4.x will return info.mem_unit==0.  Kernels after
+	 * 2.4.x actually fill this value in */
+	if (info.mem_unit==0) {
+		/* Looks like we have a kernel prior to Linux 2.4.x */
+		info.mem_unit=1024;
+		info.totalram/=info.mem_unit;
+		info.freeram/=info.mem_unit;
+		info.totalswap/=info.mem_unit;
+		info.freeswap/=info.mem_unit;
+		info.sharedram/=info.mem_unit;
+		info.bufferram/=info.mem_unit;
+	} else {
+		/* Bah. Linux 2.4.x completely changed sysinfo. This can in theory
+		overflow a 32 bit unsigned long, but who puts more then 4GiB ram+swap
+		on an embedded system? */
+		info.mem_unit/=1024;
+		info.totalram*=info.mem_unit;
+		info.freeram*=info.mem_unit;
+		info.totalswap*=info.mem_unit;
+		info.freeswap*=info.mem_unit;
+		info.sharedram*=info.mem_unit;
+		info.bufferram*=info.mem_unit;
+	}
 	if (argc > 1 && **(argv + 1) == '-') {
 		usage("free\n"
 #ifndef BB_FEATURE_TRIVIAL_HELP
@@ -61,3 +77,5 @@
 			info.freeram+info.freeswap);
 	return(TRUE);
 }
+
+
diff --git a/init.c b/init.c
index 98a58f2..9289b86 100644
--- a/init.c
+++ b/init.c
@@ -42,7 +42,6 @@
 #include <linux/version.h>
 #include <linux/reboot.h>
 #include <linux/unistd.h>
-#include <sys/sysinfo.h>		/* For check_free_memory() */
 #include <sys/fcntl.h>
 #include <sys/ioctl.h>
 #include <sys/mount.h>
@@ -270,13 +269,28 @@
 {
 	struct sysinfo info;
 
+	/* Pre initialize mem_unit in case this kernel is something prior to
+	 * the linux 2.4 kernel (which will actually fill in mem_unit... */
 	sysinfo(&info);
 	if (sysinfo(&info) != 0) {
-		message(LOG, "Error checking free memory: %s\n", strerror(errno));
+		printf("Error checking free memory: %s\n", strerror(errno));
 		return -1;
 	}
+	if (info.mem_unit==0) {
+		/* Looks like we have a kernel prior to Linux 2.4.x */
+		info.mem_unit=1024;
+		info.totalram/=info.mem_unit;
+		info.totalswap/=info.mem_unit;
+	} else {
+		/* Bah. Linux 2.4.x completely changed sysinfo. This can in theory
+		overflow a 32 bit unsigned long, but who puts more then 4GiB ram+swap
+		on an embedded system? */
+		info.mem_unit/=1024;
+		info.totalram*=info.mem_unit;
+		info.totalswap*=info.mem_unit;
+	}
 
-	return((info.totalram+info.totalswap)/1024);
+	return(info.totalram+info.totalswap);
 }
 
 static void console_init()
diff --git a/init/init.c b/init/init.c
index 98a58f2..9289b86 100644
--- a/init/init.c
+++ b/init/init.c
@@ -42,7 +42,6 @@
 #include <linux/version.h>
 #include <linux/reboot.h>
 #include <linux/unistd.h>
-#include <sys/sysinfo.h>		/* For check_free_memory() */
 #include <sys/fcntl.h>
 #include <sys/ioctl.h>
 #include <sys/mount.h>
@@ -270,13 +269,28 @@
 {
 	struct sysinfo info;
 
+	/* Pre initialize mem_unit in case this kernel is something prior to
+	 * the linux 2.4 kernel (which will actually fill in mem_unit... */
 	sysinfo(&info);
 	if (sysinfo(&info) != 0) {
-		message(LOG, "Error checking free memory: %s\n", strerror(errno));
+		printf("Error checking free memory: %s\n", strerror(errno));
 		return -1;
 	}
+	if (info.mem_unit==0) {
+		/* Looks like we have a kernel prior to Linux 2.4.x */
+		info.mem_unit=1024;
+		info.totalram/=info.mem_unit;
+		info.totalswap/=info.mem_unit;
+	} else {
+		/* Bah. Linux 2.4.x completely changed sysinfo. This can in theory
+		overflow a 32 bit unsigned long, but who puts more then 4GiB ram+swap
+		on an embedded system? */
+		info.mem_unit/=1024;
+		info.totalram*=info.mem_unit;
+		info.totalswap*=info.mem_unit;
+	}
 
-	return((info.totalram+info.totalswap)/1024);
+	return(info.totalram+info.totalswap);
 }
 
 static void console_init()
diff --git a/internal.h b/internal.h
index 127460f..c39dfde 100644
--- a/internal.h
+++ b/internal.h
@@ -30,6 +30,7 @@
 #include <stdarg.h>
 #include <string.h>
 #include <unistd.h>
+#include <errno.h>
 #include <sys/stat.h>
 #include <sys/param.h>
 #include <mntent.h>
@@ -296,9 +297,27 @@
 #define RB_POWER_OFF   0x4321fedc
 #endif
 
+/* Include our own copy of struct sysinfo to avoid binary compatability
+ * problems with Linux 2.4, which changed things.  Grumble, grumble. */
+struct sysinfo {
+	long uptime;			/* Seconds since boot */
+	unsigned long loads[3];		/* 1, 5, and 15 minute load averages */
+	unsigned long totalram;		/* Total usable main memory size */
+	unsigned long freeram;		/* Available memory size */
+	unsigned long sharedram;	/* Amount of shared memory */
+	unsigned long bufferram;	/* Memory used by buffers */
+	unsigned long totalswap;	/* Total swap space size */
+	unsigned long freeswap;		/* swap space still available */
+	unsigned short procs;		/* Number of current processes */
+	unsigned long totalhigh;	/* Total high memory size */
+	unsigned long freehigh;		/* Available high memory size */
+	unsigned int mem_unit;		/* Memory unit size in bytes */
+	char _f[20-2*sizeof(long)-sizeof(int)];	/* Padding: libc5 uses this.. */
+};
+extern int sysinfo (struct sysinfo* info);
 
-#ifndef setbit
 /* Bit map related macros -- libc5 doens't provide these... sigh.  */
+#ifndef setbit
 #define NBBY            CHAR_BIT
 #define setbit(a,i)     ((a)[(i)/NBBY] |= 1<<((i)%NBBY))
 #define clrbit(a,i)     ((a)[(i)/NBBY] &= ~(1<<((i)%NBBY)))
diff --git a/mount.c b/mount.c
index 76f048b..292be0c 100644
--- a/mount.c
+++ b/mount.c
@@ -358,6 +358,10 @@
 			fatalError( "\nDEVMTAB_GET_MOUNTS: %s\n", strerror (errno));
 
 		for( i = 0 ; i < numfilesystems ; i++) {
+			/* klude around Linux 2.4.x stupidity */
+			if (strcmp(mntentlist[i].mnt_fsname, "none") == 0) {
+				continue;
+			}
 			fprintf( stdout, "%s %s %s %s %d %d\n", mntentlist[i].mnt_fsname,
 					mntentlist[i].mnt_dir, mntentlist[i].mnt_type, 
 					mntentlist[i].mnt_opts, mntentlist[i].mnt_freq, 
@@ -380,6 +384,10 @@
 
 			while ((m = getmntent(mountTable)) != 0) {
 				char *blockDevice = m->mnt_fsname;
+				/* klude around Linux 2.4.x stupidity */
+				if (strcmp(blockDevice, "none") == 0) {
+					continue;
+				}
 				if (strcmp(blockDevice, "/dev/root") == 0) {
 					find_real_root_device_name( blockDevice);
 				}
diff --git a/procps/free.c b/procps/free.c
index a81189b..997430b 100644
--- a/procps/free.c
+++ b/procps/free.c
@@ -23,20 +23,36 @@
 
 #include "internal.h"
 #include <stdio.h>
-#include <sys/sysinfo.h>
+#include <errno.h>
 
-#define DIVISOR	1024
+
 extern int free_main(int argc, char **argv)
 {
 	struct sysinfo info;
 	sysinfo(&info);
-	info.totalram/=DIVISOR;
-	info.freeram/=DIVISOR;
-	info.totalswap/=DIVISOR;
-	info.freeswap/=DIVISOR;
-	info.sharedram/=DIVISOR;
-	info.bufferram/=DIVISOR;
-
+	/* Kernels prior to 2.4.x will return info.mem_unit==0.  Kernels after
+	 * 2.4.x actually fill this value in */
+	if (info.mem_unit==0) {
+		/* Looks like we have a kernel prior to Linux 2.4.x */
+		info.mem_unit=1024;
+		info.totalram/=info.mem_unit;
+		info.freeram/=info.mem_unit;
+		info.totalswap/=info.mem_unit;
+		info.freeswap/=info.mem_unit;
+		info.sharedram/=info.mem_unit;
+		info.bufferram/=info.mem_unit;
+	} else {
+		/* Bah. Linux 2.4.x completely changed sysinfo. This can in theory
+		overflow a 32 bit unsigned long, but who puts more then 4GiB ram+swap
+		on an embedded system? */
+		info.mem_unit/=1024;
+		info.totalram*=info.mem_unit;
+		info.freeram*=info.mem_unit;
+		info.totalswap*=info.mem_unit;
+		info.freeswap*=info.mem_unit;
+		info.sharedram*=info.mem_unit;
+		info.bufferram*=info.mem_unit;
+	}
 	if (argc > 1 && **(argv + 1) == '-') {
 		usage("free\n"
 #ifndef BB_FEATURE_TRIVIAL_HELP
@@ -61,3 +77,5 @@
 			info.freeram+info.freeswap);
 	return(TRUE);
 }
+
+
diff --git a/procps/uptime.c b/procps/uptime.c
index 67ca34d..efdbccf 100644
--- a/procps/uptime.c
+++ b/procps/uptime.c
@@ -31,13 +31,14 @@
 #include "internal.h"
 #include <stdio.h>
 #include <time.h>
-#include <sys/sysinfo.h>
+#include <errno.h>
 
 #define FSHIFT          16              /* nr of bits of precision */
 #define FIXED_1         (1<<FSHIFT)     /* 1.0 as fixed-point */
 #define LOAD_INT(x) ((x) >> FSHIFT)
 #define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
 
+
 extern int uptime_main(int argc, char **argv)
 {
 	int updays, uphours, upminutes;
diff --git a/uptime.c b/uptime.c
index 67ca34d..efdbccf 100644
--- a/uptime.c
+++ b/uptime.c
@@ -31,13 +31,14 @@
 #include "internal.h"
 #include <stdio.h>
 #include <time.h>
-#include <sys/sysinfo.h>
+#include <errno.h>
 
 #define FSHIFT          16              /* nr of bits of precision */
 #define FIXED_1         (1<<FSHIFT)     /* 1.0 as fixed-point */
 #define LOAD_INT(x) ((x) >> FSHIFT)
 #define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
 
+
 extern int uptime_main(int argc, char **argv)
 {
 	int updays, uphours, upminutes;
diff --git a/util-linux/mount.c b/util-linux/mount.c
index 76f048b..292be0c 100644
--- a/util-linux/mount.c
+++ b/util-linux/mount.c
@@ -358,6 +358,10 @@
 			fatalError( "\nDEVMTAB_GET_MOUNTS: %s\n", strerror (errno));
 
 		for( i = 0 ; i < numfilesystems ; i++) {
+			/* klude around Linux 2.4.x stupidity */
+			if (strcmp(mntentlist[i].mnt_fsname, "none") == 0) {
+				continue;
+			}
 			fprintf( stdout, "%s %s %s %s %d %d\n", mntentlist[i].mnt_fsname,
 					mntentlist[i].mnt_dir, mntentlist[i].mnt_type, 
 					mntentlist[i].mnt_opts, mntentlist[i].mnt_freq, 
@@ -380,6 +384,10 @@
 
 			while ((m = getmntent(mountTable)) != 0) {
 				char *blockDevice = m->mnt_fsname;
+				/* klude around Linux 2.4.x stupidity */
+				if (strcmp(blockDevice, "none") == 0) {
+					continue;
+				}
 				if (strcmp(blockDevice, "/dev/root") == 0) {
 					find_real_root_device_name( blockDevice);
 				}
diff --git a/utility.c b/utility.c
index b9c7a76..061ba64 100644
--- a/utility.c
+++ b/utility.c
@@ -131,6 +131,15 @@
 }
 #endif                                                 /* BB_INIT */
 
+
+
+#if defined BB_FREE || defined BB_INIT || defined BB_UNAME || defined BB_UPTIME
+#include <sys/syscall.h>
+_syscall1(int, sysinfo, struct sysinfo *, info);
+#endif                                                 /* BB_INIT */
+
+
+
 #if defined (BB_CP_MV) || defined (BB_DU)
 
 #define HASH_SIZE	311		/* Should be prime */
@@ -1189,6 +1198,9 @@
 		return 0;
 
 	while ((mountEntry = getmntent(mountTable)) != 0) {
+		if (strcmp(mountEntry->mnt_fsname, "none") == 0) {
+			continue;
+		}
 		if (strcmp(name, mountEntry->mnt_dir) == 0
 			|| strcmp(name, mountEntry->mnt_fsname) == 0)	/* String match. */
 			break;