Bug 161481

Summary: [libc] mount(2) fails with ENAMETOOLONG with path shorter than 255 // 1023 characters
Product: Base System Reporter: Enji Cooper <ngie>
Component: kernAssignee: freebsd-bugs (Nobody) <bugs>
Status: Closed FIXED    
Severity: Affects Only Me CC: as, emaste, freebsd.org, freebsd, maciej, n_hibma, taf
Priority: Normal    
Version: Unspecified   
Hardware: Any   
OS: Any   
See Also: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=167105

Description Enji Cooper freebsd_committer freebsd_triage 2011-10-11 08:40:01 UTC
mount(2) claims that it should fail with ENAMETOOLONG if the path is <= 255 or 1023 characters:

     [ENAMETOOLONG]     A component of a pathname exceeded 255 characters, or
                        the entire length of a path name exceeded 1023 charac-
                        ters.

In practice, that isn't true:

# mount -t nullfs -o noatime /nfs/scratch/freenas-2/freenas/projects/freenas8-plugins/obj.amd64/ports/distfiles /nfs/scratch/freenas-2/freenas/projects/freenas8-plugins/obj.amd64/_.w/usr/ports/distfiles
mount_nullfs: File name too long
# echo mount -t nullfs -o noatime /nfs/scratch/freenas-2/freenas/projects/freenas8-plugins/obj.amd64/ports/distfiles/ /nfs/scratch/freenas-2/freenas/projects/freenas8-plugins/obj.amd64/_.w/usr/ports/distfiles | awk '{ print length($(NF - 1)), length($NF) }'
83 90
#

It's failing a bounds check in nmount and its callees. I've seen this issue with both nullfs and UFS, so it's most likely a problem in vfs_mount.c, etc.

I've seen this issue on 8.2-RELEASE (FreeNAS), as well as 9-CURRENT and 10-CURRENT.

How-To-Repeat: #!/bin/sh

set -e

mkdir -p /nfs/scratch/freenas-2/freenas/projects/freenas8-plugins/obj.amd64/_.w/usr/ports/distfiles/
md=$(mdconfig -a -t swap -s 512m)
newfs /dev/$md
mount /dev/$md /nfs/scratch/freenas-2/freenas/projects/freenas8-plugins/obj.amd64/_.w/usr/ports/distfiles/
Comment 1 Enji Cooper freebsd_committer freebsd_triage 2011-10-11 08:57:44 UTC
I looked into this more closely after I submitted the bug and the problem 
is the arbitrarily short value attached to MNAMELEN:

122537   mckusick #define       MNAMELEN        88              /* size of 
on/from name bufs */

The value has changed over the years (all the way back to the mid-90s) 
from 90 to 70 to 80 to 88, but each time the author doesn't clearly state 
why the change was required.

Testing out a kernel with the newly imposed limits to determine if the new 
limit is functional and/or there's a major performance regression with the 
new limit.

Thanks,
-Garrett
Comment 2 Bruce Evans freebsd_committer freebsd_triage 2011-10-11 11:44:05 UTC
On Tue, 11 Oct 2011, Garrett Cooper wrote:

>> Description:
> mount(2) claims that it should fail with ENAMETOOLONG if the path is <= 255 or 1023 characters:
>
>     [ENAMETOOLONG]     A component of a pathname exceeded 255 characters, or
>                        the entire length of a path name exceeded 1023 charac-
>                        ters.
>
> In practice, that isn't true:
>
> # mount -t nullfs -o noatime /nfs/scratch/freenas-2/freenas/projects/freenas8-plugins/obj.amd64/ports/distfiles /nfs/scratch/freenas-2/freenas/projects/freenas8-plugins/obj.amd64/_.w/usr/ports/distfiles
> mount_nullfs: File name too long

There are limit of MNAMELEN = 88 and OMNAMELEN (a bit smaller) for mount().

This seems to be a new bug.  In FreeBSD[1-4], mount() used normal pathname
stuff and there were no references to MNAMELEN in vfs, and file systems
like ffs silently ignored the ENAMETOOLONG error for copyinstr() of
the user pathname to mp->mnt_stat.f_mntonname[] (which has size MNAMELEN).
(This copyinstr() isn't quite normal pathname stuff either.  copyinstr()
to the namei buffer and using that for everything in the kernel would be
normal.  ffs_mount() also handles namei initialization and I think it
uses a separate copyinstr() for f_mntonname[] in at least some cases
because namei initialization occurs later.)

This seems to have been broken somewhere in FreeBSD-5, probably
collaterally with nmount().  FreeBSD-5.2 has up-front checks that the
pathname fits in a buffer of size MNAMELEN, in both nmount(9) and mount(9).
For nmount(), the pathname is far from normal, since it is passed as
an option string.  For mount(), the pathname starts as normal, but it is
copyinstr()'ed into a malloc buffer of size MNAMELEN to join up with
nmount().  ffs no longer (still in 5.2) copyinstr()'s the user path to
mp->mnt_stat.f_mntonname[], but it still does an NDINIT() on args.fspec
with arg UIO_USERSPACE; that now seems to work only accidentally, since
the path is actually in UIO_SYSSPACE.  Now in -current, even more of the
initialization has been moved up to vfs;  ffs still does the NDINIT() but
it uses arg UIO_SYSSPACE.

ISTR old PRs about almost the opposite problem, that you could mount things
with paths so long that you couldn't easily unmount them, because the
pathname in mp->mnt_stat.f_mntonname[] is truncated.

Bruce
Comment 3 Nick Hibma freebsd_committer freebsd_triage 2014-06-18 21:29:18 UTC
In our NanoBSD builds we use nullfs to provide a dir with packages to install. I changed the MAKEOBJDIR to include the full path to working directory to provide a means to separate multiple images and versions of images.

nullfs couldn't handle the length of that path. I am -very- surprised that nullfs is not able to handle MAX_PATH length paths.

Our workaround is the use the MD5 of the full path instead of the path itself:

# XXX Take the MD5 of the path as mount_nullfs (for packages) does not handle long paths. See
# FreeBSD PR kern/161481.
p="$(echo "$PWD" | sed -Ee 's#^/((usr)?/home|Users)/##' | md5)"
NANO_OBJ=${NANO_OBJ:-"/usr/obj/nanobsd/$p"}

I do understand that this is actually a rather complex little bug, as it goes across a number of APIs.
Comment 4 Thomas Alexander Frederiksen 2015-05-04 10:57:51 UTC
I'm hitting this issue as well. On my boxes running an rsync-based backup of !FreeBSD systems to ZFS on FreeBSD, paths will routinely pass the 88 char limit in MNAMELEN. I expect that the issue also affects a large number of people running virtual systems and/or build hosts on ZFS, but also that they make workarounds without complaining.

It looks like a discussion is also ongoing on the hackers list: http://marc.info/?l=freebsd-hackers&m=141485641407039&w=2
Comment 5 Ed Maste freebsd_committer freebsd_triage 2017-04-24 14:03:50 UTC
See the ino64 64-bit inode status update and call for testing: https://lists.freebsd.org/pipermail/freebsd-current/2017-April/065687.html
Comment 6 Eitan Adler freebsd_committer freebsd_triage 2018-05-20 23:51:51 UTC
For bugs matching the following conditions:
- Status == In Progress
- Assignee == "bugs@FreeBSD.org"
- Last Modified Year <= 2017

Do
- Set Status to "Open"
Comment 7 Andriy Gapon freebsd_committer freebsd_triage 2018-05-21 06:29:22 UTC
Should be fixed now.