1 /*
2 * Copyright (c) 2024 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <strings.h>
28 #include <sys/attr.h>
29 #include <sys/param.h>
30 #include <sys/mount.h>
31
32 static int
__statfs_ext_default(const char * path,int fd,struct statfs * buf)33 __statfs_ext_default(const char *path, int fd, struct statfs *buf)
34 {
35 int ret = 0;
36
37 if (path) {
38 ret = statfs(path, buf);
39 } else {
40 ret = fstatfs(fd, buf);
41 }
42
43 return ret;
44 }
45
46 static int
__statfs_ext_noblock(const char * path,int fd,struct statfs * buf)47 __statfs_ext_noblock(const char *path, int fd, struct statfs *buf)
48 {
49 int ret = 0;
50 char *ptr;
51
52 struct {
53 uint32_t size;
54 attribute_set_t f_attrs;
55 fsid_t f_fsid;
56 uint32_t f_type;
57 attrreference_t f_mntonname;
58 uint32_t f_flags;
59 attrreference_t f_mntfromname;
60 uint32_t f_flags_ext;
61 attrreference_t f_fstypename;
62 uint32_t f_fssubtype;
63 uid_t f_owner;
64 char f_mntonname_buf[MAXPATHLEN];
65 char f_mntfromname_buf[MAXPATHLEN];
66 char f_fstypename_buf[MFSTYPENAMELEN];
67 } __attribute__((aligned(4), packed)) *attrbuf;
68
69 struct attrlist al = {
70 .bitmapcount = ATTR_BIT_MAP_COUNT,
71 .commonattr = ATTR_CMN_FSID | ATTR_CMN_RETURNED_ATTRS,
72 .volattr = ATTR_VOL_INFO | ATTR_VOL_FSTYPE | ATTR_VOL_MOUNTPOINT |
73 ATTR_VOL_MOUNTFLAGS | ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_FSTYPENAME |
74 ATTR_VOL_FSSUBTYPE | ATTR_VOL_MOUNTEXTFLAGS | ATTR_VOL_OWNER,
75 };
76
77 attrbuf = malloc(sizeof(*attrbuf));
78 if (attrbuf == NULL) {
79 errno = ENOMEM;
80 return -1;
81 }
82 bzero(attrbuf, sizeof(*attrbuf));
83
84 if (path) {
85 ret = getattrlist(path, &al, attrbuf, sizeof(*attrbuf), FSOPT_NOFOLLOW | FSOPT_RETURN_REALDEV);
86 } else {
87 ret = fgetattrlist(fd, &al, attrbuf, sizeof(*attrbuf), FSOPT_RETURN_REALDEV);
88 }
89
90 if (ret < 0) {
91 goto out;
92 }
93
94 /* Update user structure */
95 if (attrbuf->f_attrs.commonattr & ATTR_CMN_FSID) {
96 buf->f_fsid = attrbuf->f_fsid;
97 }
98 if (attrbuf->f_attrs.volattr & ATTR_VOL_OWNER) {
99 buf->f_owner = attrbuf->f_owner;
100 }
101 if (attrbuf->f_attrs.volattr & ATTR_VOL_FSTYPE) {
102 buf->f_type = attrbuf->f_type;
103 }
104 if (attrbuf->f_attrs.volattr & ATTR_VOL_MOUNTFLAGS) {
105 buf->f_flags = attrbuf->f_flags;
106 }
107 if (attrbuf->f_attrs.volattr & ATTR_VOL_FSSUBTYPE) {
108 buf->f_fssubtype = attrbuf->f_fssubtype;
109 }
110 if (attrbuf->f_attrs.volattr & ATTR_VOL_FSTYPENAME) {
111 ptr = (char *)&attrbuf->f_fstypename + attrbuf->f_fstypename.attr_dataoffset;
112 strlcpy(buf->f_fstypename, ptr, sizeof(buf->f_fstypename));
113 }
114 if (attrbuf->f_attrs.volattr & ATTR_VOL_MOUNTPOINT) {
115 ptr = (char *)&attrbuf->f_mntonname + attrbuf->f_mntonname.attr_dataoffset;
116 strlcpy(buf->f_mntonname, ptr, sizeof(buf->f_mntonname));
117 }
118 if (attrbuf->f_attrs.volattr & ATTR_VOL_MOUNTEDDEVICE) {
119 ptr = (char *)&attrbuf->f_mntfromname + attrbuf->f_mntfromname.attr_dataoffset;
120 strlcpy(buf->f_mntfromname, ptr, sizeof(buf->f_mntfromname));
121 }
122 if (attrbuf->f_attrs.volattr & ATTR_VOL_MOUNTEXTFLAGS) {
123 buf->f_flags_ext = attrbuf->f_flags_ext;
124 }
125
126 out:
127 free(attrbuf);
128 return ret;
129 }
130
131 static int
__statfs_ext_impl(const char * path,int fd,struct statfs * buf,int flags)132 __statfs_ext_impl(const char *path, int fd, struct statfs *buf, int flags)
133 {
134 int ret = 0;
135
136 bzero(buf, sizeof(struct statfs));
137
138 /* Check for invalid flags */
139 if (flags & ~(STATFS_EXT_NOBLOCK)) {
140 errno = EINVAL;
141 return -1;
142 }
143
144 /* Simply wrap statfs() or fstatfs() if no option is provided */
145 if (flags == 0) {
146 return __statfs_ext_default(path, fd, buf);
147 }
148
149 /* Retrieve filesystem statistics with extended options */
150 if (flags & STATFS_EXT_NOBLOCK) {
151 ret = __statfs_ext_noblock(path, fd, buf);
152 }
153
154 return ret;
155 }
156
157 int
fstatfs_ext(int fd,struct statfs * buf,int flags)158 fstatfs_ext(int fd, struct statfs *buf, int flags)
159 {
160 /* fstatfs() sanity checks */
161 if (fd < 0) {
162 errno = EBADF;
163 return -1;
164 }
165 if (buf == NULL) {
166 errno = EFAULT;
167 return -1;
168 }
169
170 return __statfs_ext_impl(NULL, fd, buf, flags);
171 }
172
173 int
statfs_ext(const char * path,struct statfs * buf,int flags)174 statfs_ext(const char *path, struct statfs *buf, int flags)
175 {
176 /* statfs() sanity checks */
177 if (path == NULL) {
178 errno = EFAULT;
179 return -1;
180 }
181 if (buf == NULL) {
182 errno = EFAULT;
183 return -1;
184 }
185
186 return __statfs_ext_impl(path, -1, buf, flags);
187 }
188