How to increase MNAMELEN on FreeBSD 4-stable

This web page describes how to increase the MNAMELEN limit on FreeBSD 4-stable.

Why would you want to do that, and what is MNAMELEN anyway? Well, MNAMELEN is a constant that specifies the maximum length (in characters) for mount points and for mounted filesystem nodes. A mount point is where you mount something, and a a filesystem node is what you mount there. Look at the output of df:

Filesystem    1K-blocks     Used   Avail Capacity  Mounted on
/dev/da0s1a       99183    38815   52434    43%    /
/dev/da0s1e      198399    17193  165335     9%    /var
/dev/da0s1f     2032623  1409744  460270    75%    /usr
/dev/da0s1g    25625747 16364395 7211293    69%    /archive
mfs:40           100750       54  100696     0%    /tmp
procfs                4        4       0   100%    /proc
fs:/usr/local   2032623  1363256  506758    73%    /usr/local
fs:/usr/X11R6   2032623  1363256  506758    73%    /usr/X11R6
fs:/home       10714032  7119312 2737598    72%    /home

The filesystem nodes are displayed in the leftmost column. As you can see, this can be the path of a local device node (usually in /dev), remote NFS host:path specification, an MFS process, or a synthetic filesystem such as procfs. The mount points are displayed in the rightmost column. These are always paths in the local filesystem tree.

Now what is the limit on the lengths of those? Look at /usr/include/sys/mount.h (this is an excerpt, with spacing added for readability):

        #ifdef __i386__
        #   define MNAMELEN    80
        #endif

        #ifdef __alpha__
        #   define MNAMELEN    72
        #endif

This raises two questions: Why do i386 and alpha have different limits? And shouldn't 80 (or even 72) characters be enough for everybody?

For the first question: The value of MNAMELEN is chosen so that the size of the statfs structure (also defined in <sys/mount.h>) is exactly 256 bytes, so it will align nicely on memory page boundaries. The alpha platform is a 64-bit architecture, so some elements of the statfs structure take more space. Therefore, MNAMELEN is a bit smaller in order to compensate for that, so that the overall size stays 256 bytes.

But still, shouldn't 80 (or 72) characters be more than enough? In the above example, the longest filesystem node takes 14 characters. For most needs, it is indeed enough, but there are cases where you run into trouble.

Suppose there's a big ISP with computing centres in several countries and cities. It has a large network infrastructure with fileserver farms, webhosting farms etc. The names of the fileservers are constructed by using the name of the country and city, and possibly further information such as the room number. For example, this might be the name of a fileserver:

filer08-munich-germany.nas-farm.intranet.isp.com

The space on the fileservers is allocated to various departments of the company. The web hosting department, for example, uses mount points for each virtual webserver (so they can be easily moved from one machine to another):

/vol/vol0/dept730/webhosting/virtual/somecustomersdomainname.net

As you can see, the total filesystem mount specification would be 113 characters in this case. This is not a contrived example, but a real-world situation. Putting CNAMEs into the DNS is not an option, because DNS is managed by a different department. Hardcoding the IP address in /etc/hosts would be a gross hack. Shortening the path isn't possible either, because it's already used in too many different places. Replacing the domainname with a number would introduce a superfluous level of indirection that would just make administration harder.

So far, the ISP uses only Solaris and Linux machines, which don't have an 80 characters limit. Introducing FreeBSD as an alternative operating system will be pretty hard if you can't even mount some filesystems. 80 characters enough for everybody?

Warning

The patch described on this page will increase the limit to 208 characters (i386) or 200 characters (alpha), repectively. BUT: It will break binary compatibility for some programs. Not for all, but for some. Notably, you'll have to recompile any non-standard shells such as bash and zsh, Tools that do things to your file systems such as backup programs (e.g. Amanda), and probably a few other things. On the bright side, you will be able to continue using your old Netscape 4.x binary. ;-)

To be more exact: All programs that use the statfs(), fstatfs or fhstatfs() system calls will break, i.e. they will crash with signal 11 (SIGSEGV). You will have to recompile them from source. If the source code is not available, you've got a problem.

Of course, everything you do, you do it on your own risk. If it blows up your machine, don't blame me. You have been warned. If you're not familiar with compiling kernels and with the "make world" process, don't do it.

Before starting, make a backup of all your data. Be prepared to re-install the machine from scratch if something goes wrong.

The Patch

The following has been tested on on 4.6-stable (as of 2002-08-02), but it should work the same on any later version on the 4-stable branch.

First, make sure you have all the sources in /usr/src. This should be the same version as your currently running system. Don't try to combine an upgrade with this patch, as it would make things more complicated, and it would increase the risk that it breaks somewhere. Update first (the normal way), then continue.

Apply the following patch to /usr/src/sys/sys/mount.h. You can download the patch here (probably better than copy&paste it from the page, which would destroy tabs).


--- mount.h.orig	Sat Sep 14 17:38:17 2002
+++ mount.h	Sat Sep 14 17:38:31 2002
@@ -70,10 +70,10 @@
 
 #define MFSNAMELEN	16	/* length of fs type name, including null */
 #ifdef __i386__
-#define	MNAMELEN	80	/* length of buffer for returned name */
+#define	MNAMELEN	208	/* length of buffer for returned name */
 #endif
 #ifdef __alpha__
-#define	MNAMELEN	72	/* length of buffer for returned name */
+#define	MNAMELEN	200	/* length of buffer for returned name */
 #endif
 
 struct statfs {

Also copy the patched file to /usr/include/sys/mount.h. Actually that shouldn't be necessary, as the build process should not use it, but you never know.

Now start the standard update procedure:

	cd /usr/src
        make buildworld
        make buildkernel KERNCONF=YOUR_KERNEL_HERE
        make installkernel KERNCONF=YOUR_KERNEL_HERE

Now it gets a little more complicated. You cannot make the "installworld" in multi-user mode, because of the binary incompatibilities of the new world. But for the same reason, you cannot make it in single-user mode directly either -- you wouldn't even be able to use the fsck and mount commands.

There are several possible solutions. If you have another FreeBSD machine, you could plug the harddisk into it and make the installworld there with DESTDIR=/foo (whith /foo being the directory where you've mounted the harddisk). Another possibility is boot from a Live-FS CD-ROM ("Fixit" CD), mount everything somewhere and do the installworld. I haven't tried any of those ways, but they should work.

The following description applies if you don't have a second machine, and you cannot (or don't want to) boot from a CD-ROM.

First, you'll need a few binaries to bootstrap single-user mode with the new kernel. Copy them from the object tree to your /root directory. These are the binaries that you will need (hopefully I didn't forget any):

        /usr/obj/usr/src/sbin/fsck/fsck
        /usr/obj/usr/src/sbin/mount/mount
        /usr/obj/usr/src/usr.bin/xinstall/xinstall
	/usr/obj/usr/src/bin/sh/sh

Additionally, copy the xinstall binary to /usr/obj/usr/src/i386/usr/bin/install (note, there's no "x" in the target, just "install"). Don't ask me why, but it's necessary.

Now it's time to reboot. Note that it is not sufficient to shutdown into single-user mode. You have to reboot the machine to enable the new kernel. Be sure to reboot into single-user mode, i.e. abort the bootloader countdown with the space key and type "boot -s" at the boot loader prompt.

When the init(8) process asks for the single-user mode shell, you have to use the new sh binary, therefore answer "/root/sh".

First check the filesystems and mount those that your need. You have to use the new binaries:

        # /root/fsck -p
        # /root/mount -u -o rw /
        # /root/mount /usr

If you have /usr/src or /usr/obj on separate partitions, mount them also.

Note: This is the point of no return. You can still boot /kernel.old, and you still haven't overwritten anything of your userland. If you proceed now, your userland will be overwritten with the new (incompatible) binaries. The only way back is to revert the patch to mount.h and repeat the whole process, or re-install the machine from scratch.

Copy the new binaries that you have stored in /root into your system:

        # cp /root/sh /bin
        # cp /root/xinstall /usr/bin/install

Next, disable the installation of perl, because it will break. Again, don't ask me why, but trust me, it will break. Don't worry, you can install it later after the rest of the system has been installed properly. The following command will do.

        # echo "NOPERL=true" >> /etc/make.conf

Now initiate the installation and keep your fingers crossed:

        # cd /usr/src
        # make installworld

If something breaks along the way, you will probably see a kernel message saying that some program died with signal 11 (SIGSEGV). If that happens, copy that binary from the object tree (/usr/obj/usr/src/...) to the final location on your system, and let me know about it so I can update this webpage. Then just restart the "installworld".

If everything went fine, then remove the "NOPERL" line from /etc/make.conf and install perl. If that doesn't work, try this:

        # cd /usr/src/gnu/usr.bin/perl
        # make clean
        # cd /usr/src
        # make -f Makefile.inc1 _libraries
        # make -f Makefile.inc1 _depend
        # make -f Makefile.inc1 everything
        # make installworld

There's probably a more elegant solution, but the above will work. Alternatively you can install perl from the ports collection, of course.

You're now ready to go multi-user by exitting then single-user mode shell. The next thing you should do is to recompile a few ports. For example, non-standard shells will need recompilation.


[Valid XHTML 1.0]