/* Get file-specific information about a file. Linux version. Copyright (C) 1991-2014 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 . */ #include #include #include #include #include #include #include "pathconf.h" #include "linux_fsinfo.h" #include static long int posix_pathconf (const char *file, int name); /* Define this first, so it can be inlined. */ #define __pathconf static posix_pathconf #include /* 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 }