mirror of
https://sourceware.org/git/glibc.git
synced 2024-12-03 10:21:05 +00:00
d755bba40f
Fix BZ #15305. On kernel versions earlier than 2.6.29, the Linux kernel exported a sysctl called restrict_chown for xfs, which could be used to allow chown to users other than the owner. 2.6.29 removed this support, causing the open_not_cancel_2 to fail and thus modify errno. The fix is to save and restore errno so that the caller sees it as unmodified. Additionally, since the code to check the sysctl is not useful on newer kernels, we add an ifdef so that in future the code block gets rmeoved completely.
324 lines
7.6 KiB
C
324 lines
7.6 KiB
C
/* Get file-specific information about a file. Linux version.
|
|
Copyright (C) 1991-2013 Free Software Foundation, Inc.
|
|
This file is part of the GNU C Library.
|
|
|
|
The GNU C Library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
The GNU C Library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with the GNU C Library; if not, see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include <errno.h>
|
|
#include <mntent.h>
|
|
#include <stdio_ext.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/sysmacros.h>
|
|
|
|
#include "pathconf.h"
|
|
#include "linux_fsinfo.h"
|
|
#include <not-cancel.h>
|
|
|
|
static long int posix_pathconf (const char *file, int name);
|
|
|
|
/* Define this first, so it can be inlined. */
|
|
#define __pathconf static posix_pathconf
|
|
#include <sysdeps/posix/pathconf.c>
|
|
|
|
|
|
/* Get file-specific information about FILE. */
|
|
long int
|
|
__pathconf (const char *file, int name)
|
|
{
|
|
struct statfs fsbuf;
|
|
|
|
switch (name)
|
|
{
|
|
case _PC_LINK_MAX:
|
|
return __statfs_link_max (__statfs (file, &fsbuf), &fsbuf, file, -1);
|
|
|
|
case _PC_FILESIZEBITS:
|
|
return __statfs_filesize_max (__statfs (file, &fsbuf), &fsbuf);
|
|
|
|
case _PC_2_SYMLINKS:
|
|
return __statfs_symlinks (__statfs (file, &fsbuf), &fsbuf);
|
|
|
|
case _PC_CHOWN_RESTRICTED:
|
|
return __statfs_chown_restricted (__statfs (file, &fsbuf), &fsbuf);
|
|
|
|
default:
|
|
return posix_pathconf (file, name);
|
|
}
|
|
}
|
|
|
|
|
|
static long int
|
|
distinguish_extX (const struct statfs *fsbuf, const char *file, int fd)
|
|
{
|
|
char buf[64];
|
|
char path[PATH_MAX];
|
|
struct stat64 st;
|
|
|
|
if ((file == NULL ? fstat64 (fd, &st) : stat64 (file, &st)) != 0)
|
|
/* Strange. The statfd call worked, but stat fails. Default to
|
|
the more pessimistic value. */
|
|
return EXT2_LINK_MAX;
|
|
|
|
__snprintf (buf, sizeof (buf), "/sys/dev/block/%u:%u",
|
|
gnu_dev_major (st.st_dev), gnu_dev_minor (st.st_dev));
|
|
|
|
ssize_t n = __readlink (buf, path, sizeof (path));
|
|
if (n != -1 && n < sizeof (path))
|
|
{
|
|
path[n] = '\0';
|
|
char *base = strdupa (basename (path));
|
|
__snprintf (path, sizeof (path), "/sys/fs/ext4/%s", base);
|
|
|
|
return __access (path, F_OK) == 0 ? EXT4_LINK_MAX : EXT2_LINK_MAX;
|
|
}
|
|
|
|
/* XXX Is there a better way to distinguish ext2/3 from ext4 than
|
|
iterating over the mounted filesystems and compare the device
|
|
numbers? */
|
|
FILE *mtab = __setmntent ("/proc/mounts", "r");
|
|
if (mtab == NULL)
|
|
mtab = __setmntent (_PATH_MOUNTED, "r");
|
|
|
|
/* By default be conservative. */
|
|
long int result = EXT2_LINK_MAX;
|
|
if (mtab != NULL)
|
|
{
|
|
struct mntent mntbuf;
|
|
char tmpbuf[1024];
|
|
|
|
/* No locking needed. */
|
|
(void) __fsetlocking (mtab, FSETLOCKING_BYCALLER);
|
|
|
|
while (__getmntent_r (mtab, &mntbuf, tmpbuf, sizeof (tmpbuf)))
|
|
{
|
|
if (strcmp (mntbuf.mnt_type, "ext2") != 0
|
|
&& strcmp (mntbuf.mnt_type, "ext3") != 0
|
|
&& strcmp (mntbuf.mnt_type, "ext4") != 0)
|
|
continue;
|
|
|
|
struct stat64 fsst;
|
|
if (stat64 (mntbuf.mnt_dir, &fsst) >= 0
|
|
&& st.st_dev == fsst.st_dev)
|
|
{
|
|
if (strcmp (mntbuf.mnt_type, "ext4") == 0)
|
|
result = EXT4_LINK_MAX;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Close the file. */
|
|
__endmntent (mtab);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/* Used like: return statfs_link_max (__statfs (name, &buf), &buf); */
|
|
long int
|
|
__statfs_link_max (int result, const struct statfs *fsbuf, const char *file,
|
|
int fd)
|
|
{
|
|
if (result < 0)
|
|
{
|
|
if (errno == ENOSYS)
|
|
/* Not possible, return the default value. */
|
|
return LINUX_LINK_MAX;
|
|
|
|
/* Some error occured. */
|
|
return -1;
|
|
}
|
|
|
|
switch (fsbuf->f_type)
|
|
{
|
|
case EXT2_SUPER_MAGIC:
|
|
/* Unfortunately the kernel does not return a different magic number
|
|
for ext4. This would be necessary to easily detect etx4 since it
|
|
has a different LINK_MAX value. Therefore we have to find it out
|
|
the hard way. */
|
|
return distinguish_extX (fsbuf, file, fd);
|
|
|
|
case F2FS_SUPER_MAGIC:
|
|
return F2FS_LINK_MAX;
|
|
|
|
case MINIX_SUPER_MAGIC:
|
|
case MINIX_SUPER_MAGIC2:
|
|
return MINIX_LINK_MAX;
|
|
|
|
case MINIX2_SUPER_MAGIC:
|
|
case MINIX2_SUPER_MAGIC2:
|
|
return MINIX2_LINK_MAX;
|
|
|
|
case XENIX_SUPER_MAGIC:
|
|
return XENIX_LINK_MAX;
|
|
|
|
case SYSV4_SUPER_MAGIC:
|
|
case SYSV2_SUPER_MAGIC:
|
|
return SYSV_LINK_MAX;
|
|
|
|
case COH_SUPER_MAGIC:
|
|
return COH_LINK_MAX;
|
|
|
|
case UFS_MAGIC:
|
|
case UFS_CIGAM:
|
|
return UFS_LINK_MAX;
|
|
|
|
case REISERFS_SUPER_MAGIC:
|
|
return REISERFS_LINK_MAX;
|
|
|
|
case XFS_SUPER_MAGIC:
|
|
return XFS_LINK_MAX;
|
|
|
|
case LUSTRE_SUPER_MAGIC:
|
|
return LUSTRE_LINK_MAX;
|
|
|
|
default:
|
|
return LINUX_LINK_MAX;
|
|
}
|
|
}
|
|
|
|
|
|
/* Used like: return statfs_filesize_max (__statfs (name, &buf), &buf); */
|
|
long int
|
|
__statfs_filesize_max (int result, const struct statfs *fsbuf)
|
|
{
|
|
if (result < 0)
|
|
{
|
|
if (errno == ENOSYS)
|
|
/* Not possible, return the default value. */
|
|
return 32;
|
|
|
|
/* Some error occured. */
|
|
return -1;
|
|
}
|
|
|
|
switch (fsbuf->f_type)
|
|
{
|
|
case F2FS_SUPER_MAGIC:
|
|
return 256;
|
|
|
|
case BTRFS_SUPER_MAGIC:
|
|
return 255;
|
|
|
|
case EXT2_SUPER_MAGIC:
|
|
case UFS_MAGIC:
|
|
case UFS_CIGAM:
|
|
case REISERFS_SUPER_MAGIC:
|
|
case XFS_SUPER_MAGIC:
|
|
case SMB_SUPER_MAGIC:
|
|
case NTFS_SUPER_MAGIC:
|
|
case UDF_SUPER_MAGIC:
|
|
case JFS_SUPER_MAGIC:
|
|
case VXFS_SUPER_MAGIC:
|
|
case CGROUP_SUPER_MAGIC:
|
|
case LUSTRE_SUPER_MAGIC:
|
|
return 64;
|
|
|
|
case MSDOS_SUPER_MAGIC:
|
|
case JFFS_SUPER_MAGIC:
|
|
case JFFS2_SUPER_MAGIC:
|
|
case NCP_SUPER_MAGIC:
|
|
case ROMFS_SUPER_MAGIC:
|
|
return 32;
|
|
|
|
default:
|
|
return 32;
|
|
}
|
|
}
|
|
|
|
|
|
/* Used like: return statfs_link_max (__statfs (name, &buf), &buf); */
|
|
long int
|
|
__statfs_symlinks (int result, const struct statfs *fsbuf)
|
|
{
|
|
if (result < 0)
|
|
{
|
|
if (errno == ENOSYS)
|
|
/* Not possible, return the default value. */
|
|
return 1;
|
|
|
|
/* Some error occured. */
|
|
return -1;
|
|
}
|
|
|
|
switch (fsbuf->f_type)
|
|
{
|
|
case ADFS_SUPER_MAGIC:
|
|
case BFS_MAGIC:
|
|
case CRAMFS_MAGIC:
|
|
case DEVPTS_SUPER_MAGIC:
|
|
case EFS_SUPER_MAGIC:
|
|
case EFS_MAGIC:
|
|
case MSDOS_SUPER_MAGIC:
|
|
case NTFS_SUPER_MAGIC:
|
|
case QNX4_SUPER_MAGIC:
|
|
case ROMFS_SUPER_MAGIC:
|
|
/* No symlink support. */
|
|
return 0;
|
|
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
/* Used like: return __statfs_chown_restricted (__statfs (name, &buf), &buf);*/
|
|
long int
|
|
__statfs_chown_restricted (int result, const struct statfs *fsbuf)
|
|
{
|
|
if (result < 0)
|
|
{
|
|
if (errno == ENOSYS)
|
|
/* Not possible, return the default value. */
|
|
return 1;
|
|
|
|
/* Some error occured. */
|
|
return -1;
|
|
}
|
|
|
|
#if __ASSUME_XFS_RESTRICTED_CHOWN
|
|
return 1;
|
|
#else
|
|
int fd;
|
|
int save_errno;
|
|
long int retval = 1;
|
|
switch (fsbuf->f_type)
|
|
{
|
|
case XFS_SUPER_MAGIC:
|
|
save_errno = errno;
|
|
/* Read the value from /proc/sys/fs/xfs/restrict_chown. If we cannot
|
|
read it default to assume the restriction is in place. */
|
|
fd = open_not_cancel_2 ("/proc/sys/fs/xfs/restrict_chown", O_RDONLY);
|
|
if (fd != -1)
|
|
{
|
|
char buf[2];
|
|
if (TEMP_FAILURE_RETRY (read_not_cancel (fd, buf, 2)) == 2
|
|
&& buf[0] >= '0' && buf[0] <= '1')
|
|
retval = buf[0] - '0';
|
|
|
|
close_not_cancel_no_status (fd);
|
|
}
|
|
__set_errno (save_errno);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return retval;
|
|
#endif
|
|
}
|