| /* | 
 |  * Copyright (C) 2008 The Android Open Source Project | 
 |  * All rights reserved. | 
 |  * | 
 |  * Redistribution and use in source and binary forms, with or without | 
 |  * modification, are permitted provided that the following conditions | 
 |  * are met: | 
 |  *  * Redistributions of source code must retain the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer. | 
 |  *  * Redistributions in binary form must reproduce the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer in | 
 |  *    the documentation and/or other materials provided with the | 
 |  *    distribution. | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
 |  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
 |  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | 
 |  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | 
 |  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | 
 |  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | 
 |  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS | 
 |  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | 
 |  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | 
 |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | 
 |  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
 |  * SUCH DAMAGE. | 
 |  */ | 
 |  | 
 | #include <pathconf.h> | 
 | #include <sys/vfs.h> | 
 | #include <sys/limits.h> | 
 | #include <errno.h> | 
 |  | 
 | /* these may not be defined yet by our headers */ | 
 | #ifndef _POSIX_VDISABLE | 
 | #define _POSIX_VDISABLE  -1 | 
 | #endif | 
 |  | 
 | #ifndef _POSIX_SYNC_IO | 
 | #define _POSIX_SYNC_IO  -1 | 
 | #endif | 
 |  | 
 | #ifndef _POSIX_PRIO_IO | 
 | #define _POSIX_PRIO_IO  -1 | 
 | #endif | 
 |  | 
 | #ifndef _POSIX_ASYNC_IO | 
 | #define _POSIX_ASYNC_IO  -1 | 
 | #endif | 
 |  | 
 |  | 
 | static long | 
 | __filesizebits( struct statfs*  s ) | 
 | { | 
 | #define   EOL_MAGIC   0x0000U | 
 |  | 
 |     /* list of known 64-bit aware filesystems */ | 
 |     static const uint32_t  known64[] = { | 
 |         EXT2_SUPER_MAGIC, | 
 |         UFS_MAGIC, | 
 |         REISERFS_SUPER_MAGIC, | 
 |         XFS_SUPER_MAGIC, | 
 |         SMB_SUPER_MAGIC, | 
 |         UDF_SUPER_MAGIC, | 
 |         JFS_SUPER_MAGIC, | 
 |         NTFS_SB_MAGIC, | 
 |         VXFS_SUPER_MAGIC, | 
 |         EOL_MAGIC | 
 |     }; | 
 |     int  nn = 0; | 
 |  | 
 |     for (; known64[nn] != EOL_MAGIC; ++nn) { | 
 |         if (known64[nn] == s->f_type) { | 
 |             return 64; | 
 |         } | 
 |     } | 
 |     return 32; | 
 | } | 
 |  | 
 |  | 
 | static long | 
 | __link_max( struct statfs*  s ) | 
 | { | 
 |     // These constant values were taken from kernel headers. | 
 |     // They're not available in uapi headers. | 
 |     static const struct { uint32_t  type; int  max; }  knownMax[] = | 
 |     { | 
 |         { EXT2_SUPER_MAGIC, 32000 }, | 
 |         { EXT3_SUPER_MAGIC, 32000 }, | 
 |         { MINIX_SUPER_MAGIC, 250 }, | 
 |         { MINIX2_SUPER_MAGIC, 65530 }, | 
 |         { REISERFS_SUPER_MAGIC, 0xffff - 1000 }, | 
 |         { UFS_MAGIC, 32000 }, | 
 |         { EOL_MAGIC, 0 } | 
 |     }; | 
 |     int   nn = 0; | 
 |  | 
 |     for (; knownMax[nn].type != EOL_MAGIC; ++nn) { | 
 |         if (knownMax[nn].type == s->f_type) { | 
 |             return knownMax[nn].max; | 
 |         } | 
 |     } | 
 |     return LINK_MAX; | 
 | } | 
 |  | 
 | static long | 
 | __2_symlinks( struct statfs*  s ) | 
 | { | 
 |     /* list of know filesystems that don't support symlinks */ | 
 |     static const uint32_t  knownNoSymlinks[] = { | 
 |         ADFS_SUPER_MAGIC, BFS_MAGIC, CRAMFS_MAGIC, | 
 |         EFS_SUPER_MAGIC, MSDOS_SUPER_MAGIC, NTFS_SB_MAGIC, | 
 |         QNX4_SUPER_MAGIC, | 
 |         EOL_MAGIC | 
 |     }; | 
 |     int  nn = 0; | 
 |  | 
 |     for (; knownNoSymlinks[nn] != EOL_MAGIC; ++nn) { | 
 |         if (knownNoSymlinks[nn] == s->f_type) { | 
 |             return 0; | 
 |         } | 
 |     } | 
 |     return 1; | 
 | } | 
 |  | 
 | static long | 
 | __name_max( struct statfs*  s ) | 
 | { | 
 |     return s->f_namelen; | 
 | } | 
 |  | 
 | long | 
 | pathconf(const char *path, int name) | 
 | { | 
 |     struct statfs  buf; | 
 |     int            ret = statfs( path, &buf ); | 
 |  | 
 |     if (ret < 0) | 
 |         return -1; | 
 |  | 
 |     switch (name) { | 
 |     case _PC_FILESIZEBITS: | 
 |         return __filesizebits(&buf); | 
 |  | 
 |     case _PC_LINK_MAX: | 
 |         return __link_max(&buf); | 
 |  | 
 |     case _PC_MAX_CANON: | 
 |         return MAX_CANON; | 
 |  | 
 |     case _PC_MAX_INPUT: | 
 |         return MAX_INPUT; | 
 |  | 
 |     case _PC_NAME_MAX: | 
 |         return __name_max(&buf); | 
 |  | 
 |     case _PC_PATH_MAX: | 
 |         return PATH_MAX; | 
 |  | 
 |     case _PC_PIPE_BUF: | 
 |         return PIPE_BUF; | 
 |  | 
 |     case _PC_2_SYMLINKS: | 
 |         return __2_symlinks(&buf); | 
 |  | 
 | #if 0  /* don't know what to do there, the specs are really weird */ | 
 |     case _PC_ALLOC_SIZE_MIN: | 
 |     case _PC_REC_INCR_XFER_SIZE: | 
 |     case _PC_REC_MAX_XFER_SIZE: | 
 |     case _PC_REC_MIN_XFER_SIZE: | 
 |     case _PC_REC_XFER_ALIGN: | 
 | #endif | 
 |  | 
 |     case _PC_SYMLINK_MAX: | 
 |         return -1;  /* no limit */ | 
 |  | 
 |     case _PC_CHOWN_RESTRICTED: | 
 |         return _POSIX_CHOWN_RESTRICTED; | 
 |  | 
 |     case _PC_NO_TRUNC: | 
 |         return _POSIX_NO_TRUNC; | 
 |  | 
 |     case _PC_VDISABLE: | 
 |         return _POSIX_VDISABLE; | 
 |  | 
 |     case _PC_ASYNC_IO: | 
 |         return _POSIX_ASYNC_IO; | 
 |  | 
 |     case _PC_PRIO_IO: | 
 |         return _POSIX_PRIO_IO; | 
 |  | 
 |     case _PC_SYNC_IO: | 
 |         return _POSIX_SYNC_IO; | 
 |  | 
 |     default: | 
 |         errno = EINVAL; | 
 |         return -1; | 
 |     } | 
 | } | 
 |  | 
 | long fpathconf(int fildes, int name) | 
 | { | 
 |     struct statfs  buf; | 
 |     int            ret = fstatfs(fildes, &buf); | 
 |  | 
 |     if (ret < 0) | 
 |         return -1; | 
 |  | 
 |     switch (name) { | 
 |     case _PC_FILESIZEBITS: | 
 |         return __filesizebits(&buf); | 
 |  | 
 |     case _PC_LINK_MAX: | 
 |         return __link_max(&buf); | 
 |  | 
 |     case _PC_MAX_CANON: | 
 |         return MAX_CANON; | 
 |  | 
 |     case _PC_MAX_INPUT: | 
 |         return MAX_INPUT; | 
 |  | 
 |     case _PC_NAME_MAX: | 
 |         return __name_max(&buf); | 
 |  | 
 |     case _PC_PATH_MAX: | 
 |         return PATH_MAX; | 
 |  | 
 |     case _PC_PIPE_BUF: | 
 |         return PIPE_BUF; | 
 |  | 
 |     case _PC_2_SYMLINKS: | 
 |         return __2_symlinks(&buf); | 
 |  | 
 | #if 0  /* don't know what to do there, the specs are really weird */ | 
 |     case _PC_ALLOC_SIZE_MIN: | 
 |     case _PC_REC_INCR_XFER_SIZE: | 
 |     case _PC_REC_MAX_XFER_SIZE: | 
 |     case _PC_REC_MIN_XFER_SIZE: | 
 |     case _PC_REC_XFER_ALIGN: | 
 | #endif | 
 |  | 
 |     case _PC_SYMLINK_MAX: | 
 |         return -1;  /* no limit */ | 
 |  | 
 |     case _PC_CHOWN_RESTRICTED: | 
 |         return _POSIX_CHOWN_RESTRICTED; | 
 |  | 
 |     case _PC_NO_TRUNC: | 
 |         return _POSIX_NO_TRUNC; | 
 |  | 
 |     case _PC_VDISABLE: | 
 |         return _POSIX_VDISABLE; | 
 |  | 
 |     case _PC_ASYNC_IO: | 
 |         return _POSIX_ASYNC_IO; | 
 |  | 
 |     case _PC_PRIO_IO: | 
 |         return _POSIX_PRIO_IO; | 
 |  | 
 |     case _PC_SYNC_IO: | 
 |         return _POSIX_SYNC_IO; | 
 |  | 
 |     default: | 
 |         errno = EINVAL; | 
 |         return -1; | 
 |     } | 
 | } |