ZFS and pathconf(_PC_NO_TRUNC)

Bruce Evans brde at optusnet.com.au
Thu Nov 11 13:54:41 UTC 2010


On Thu, 11 Nov 2010, Mark Blackman wrote:

> On 11 Nov 2010, at 12:06, Martin Simmons wrote:
>> Your call to printf is clobbering the real errno, which is EINVAL.
>
> Doh! thanks for pointing that out. :)
>
>> That is an
>> allowed value according to the pathconf man page:
>>
>>     [EINVAL]           The implementation does not support an association of
>>                        the variable name with the associated file.
>>
>> So it is correct, but maybe not useful.
>
> hmm. this is popping up in the context of building perl 5.12 on a zfs-only
> filesystem. One of the POSIX::* tests fails because of the above.

zfs_vnops.c:zfs_pathconf() is missing _PC_NO_TRUNC, so it seems to be just
broken (it returns EOPNOTSUPP for cases not in the switch, so there seems
to be no way for another level to support _PC_NO_TRUNC).  It apparently
depends on another layer providing defaults.

Other basic things missing in it:
_PC_NAME_MAX
_PC_CHOWN_RESTRICTED
_PC_PIPE_BUF
[several other things that are in the switch statement in vop_stdpathconf(),
  but which are nonsense there since they only apply to device files and
  should depend on the file anyway, and which don't apply to zfs or any
  normal file system since device files on normal file systems are no longer
  supported]

_PC_PIPE_BUF is not quite like the features that onluy apply to device
files.  It aplies to named pipes, and since there is no defaulting of
_PC_* in FreeBSD, all file systems that support named pipes must support
it in their pathconf vop although it has nothing to do with file systems.

Fortunately, pathconf() is never used except by naive programs like perl :-).

_PC_NAME_MAX is used by patch(1) in FreeBSD, but patch(1) also has an ifdef
tangle using _POSIX_NAME_MAX and other messes which I think allows patch
to work accidentally if zfs returns EOPNOTSUPP: from backupfile.c:

% void
% addext(char *filename, char *ext, int e)
% {
%   char *s = (char *)(uintptr_t)(const void *)basename (filename);
%   int slen = strlen (s), extlen = strlen (ext);
%   long slen_max = -1;
% 
% #if HAVE_PATHCONF && defined (_PC_NAME_MAX)
% #ifndef _POSIX_NAME_MAX
% #define _POSIX_NAME_MAX 14
% #endif

_POSIX_NAME_MAX is always 14 on POSIX systems, so this ifdef is no help.

%   if (slen + extlen <= _POSIX_NAME_MAX)
%     /* The file name is so short there's no need to call pathconf.  */
%     slen_max = _POSIX_NAME_MAX;
%   else if (s == filename)
%     slen_max = pathconf (".", _PC_NAME_MAX);

I think we get here and pathconf() fails for names of length just 15 or
greater.

%   else
%     {
%       char c = *s;
%       *s = 0;
%       slen_max = pathconf (filename, _PC_NAME_MAX);
%       *s = c;
%     }
% #endif
%   if (slen_max == -1) {
% #ifdef HAVE_LONG_FILE_NAMES
%     slen_max = 255;

We get here on error (since although FreeBSD only has long file names on
some file systems, patch is misconfigured, possibly by configuring it on
a normal file system that has long names, so HAVE_LONG_FILE_NAMES is set
unconditionally in the hard-configured config.h), so the max is essentially 
hard-coded as 255 if pathconf() fails.

% #else
%     slen_max = 14;
% #endif
%   }

Bruce


More information about the freebsd-fs mailing list