/*
* Copyright (c) 2024 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <sys/attr.h>
#include <sys/param.h>
#include <sys/mount.h>
static int
__statfs_ext_default(const char *path, int fd, struct statfs *buf)
{
int ret = 0;
if (path) {
ret = statfs(path, buf);
} else {
ret = fstatfs(fd, buf);
}
return ret;
}
static int
__statfs_ext_noblock(const char *path, int fd, struct statfs *buf)
{
int ret = 0;
char *ptr;
struct {
uint32_t size;
attribute_set_t f_attrs;
fsid_t f_fsid;
uint32_t f_type;
attrreference_t f_mntonname;
uint32_t f_flags;
attrreference_t f_mntfromname;
uint32_t f_flags_ext;
attrreference_t f_fstypename;
uint32_t f_fssubtype;
uid_t f_owner;
char f_mntonname_buf[MAXPATHLEN];
char f_mntfromname_buf[MAXPATHLEN];
char f_fstypename_buf[MFSTYPENAMELEN];
} __attribute__((aligned(4), packed)) *attrbuf;
struct attrlist al = {
.bitmapcount = ATTR_BIT_MAP_COUNT,
.commonattr = ATTR_CMN_FSID | ATTR_CMN_RETURNED_ATTRS,
.volattr = ATTR_VOL_INFO | ATTR_VOL_FSTYPE | ATTR_VOL_MOUNTPOINT |
ATTR_VOL_MOUNTFLAGS | ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_FSTYPENAME |
ATTR_VOL_FSSUBTYPE | ATTR_VOL_MOUNTEXTFLAGS | ATTR_VOL_OWNER,
};
attrbuf = malloc(sizeof(*attrbuf));
if (attrbuf == NULL) {
errno = ENOMEM;
return -1;
}
bzero(attrbuf, sizeof(*attrbuf));
if (path) {
ret = getattrlist(path, &al, attrbuf, sizeof(*attrbuf), FSOPT_NOFOLLOW | FSOPT_RETURN_REALDEV);
} else {
ret = fgetattrlist(fd, &al, attrbuf, sizeof(*attrbuf), FSOPT_RETURN_REALDEV);
}
if (ret < 0) {
goto out;
}
/* Update user structure */
if (attrbuf->f_attrs.commonattr & ATTR_CMN_FSID) {
buf->f_fsid = attrbuf->f_fsid;
}
if (attrbuf->f_attrs.volattr & ATTR_VOL_OWNER) {
buf->f_owner = attrbuf->f_owner;
}
if (attrbuf->f_attrs.volattr & ATTR_VOL_FSTYPE) {
buf->f_type = attrbuf->f_type;
}
if (attrbuf->f_attrs.volattr & ATTR_VOL_MOUNTFLAGS) {
buf->f_flags = attrbuf->f_flags;
}
if (attrbuf->f_attrs.volattr & ATTR_VOL_FSSUBTYPE) {
buf->f_fssubtype = attrbuf->f_fssubtype;
}
if (attrbuf->f_attrs.volattr & ATTR_VOL_FSTYPENAME) {
ptr = (char *)&attrbuf->f_fstypename + attrbuf->f_fstypename.attr_dataoffset;
strlcpy(buf->f_fstypename, ptr, sizeof(buf->f_fstypename));
}
if (attrbuf->f_attrs.volattr & ATTR_VOL_MOUNTPOINT) {
ptr = (char *)&attrbuf->f_mntonname + attrbuf->f_mntonname.attr_dataoffset;
strlcpy(buf->f_mntonname, ptr, sizeof(buf->f_mntonname));
}
if (attrbuf->f_attrs.volattr & ATTR_VOL_MOUNTEDDEVICE) {
ptr = (char *)&attrbuf->f_mntfromname + attrbuf->f_mntfromname.attr_dataoffset;
strlcpy(buf->f_mntfromname, ptr, sizeof(buf->f_mntfromname));
}
if (attrbuf->f_attrs.volattr & ATTR_VOL_MOUNTEXTFLAGS) {
buf->f_flags_ext = attrbuf->f_flags_ext;
}
out:
free(attrbuf);
return ret;
}
static int
__statfs_ext_impl(const char *path, int fd, struct statfs *buf, int flags)
{
int ret = 0;
bzero(buf, sizeof(struct statfs));
/* Check for invalid flags */
if (flags & ~(STATFS_EXT_NOBLOCK)) {
errno = EINVAL;
return -1;
}
/* Retrieve filesystem statistics with extended options */
if (flags & STATFS_EXT_NOBLOCK) {
ret = __statfs_ext_noblock(path, fd, buf);
}
/*
* Fall back to statfs()/fstatfs() if:
* 1. No options are provided.
* 2. __statfs_ext_noblock() returns EINVAL.
*/
if ((flags == 0) || (ret == -1 && errno == EINVAL)) {
ret = __statfs_ext_default(path, fd, buf);
}
return ret;
}
int
fstatfs_ext(int fd, struct statfs *buf, int flags)
{
/* fstatfs() sanity checks */
if (fd < 0) {
errno = EBADF;
return -1;
}
if (buf == NULL) {
errno = EFAULT;
return -1;
}
return __statfs_ext_impl(NULL, fd, buf, flags);
}
int
statfs_ext(const char *path, struct statfs *buf, int flags)
{
/* statfs() sanity checks */
if (path == NULL) {
errno = EFAULT;
return -1;
}
if (buf == NULL) {
errno = EFAULT;
return -1;
}
return __statfs_ext_impl(path, -1, buf, flags);
}