1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 /*
30  * Portions of this source code were derived from Berkeley 4.3 BSD
31  * under license from the Regents of the University of California.
32  */
33 
34 #pragma ident	"%Z%%M%	%I%	%E% SMI"
35 
36 /*
37  * Get file system statistics (statvfs and fstatvfs).
38  */
39 
40 #include <sys/types.h>
41 #include <sys/inttypes.h>
42 #include <sys/t_lock.h>
43 #include <sys/param.h>
44 #include <sys/errno.h>
45 #include <sys/fstyp.h>
46 #include <sys/systm.h>
47 #include <sys/vfs.h>
48 #include <sys/statvfs.h>
49 #include <sys/vnode.h>
50 #include <sys/file.h>
51 #include <sys/cmn_err.h>
52 #include <sys/debug.h>
53 #include <sys/pathname.h>
54 
55 #include <vm/page.h>
56 #include <fs/fs_subr.h>
57 
58 #define	STATVFSCOPY(dst, src)					\
59 	(dst)->f_bsize	= (src)->f_bsize;			\
60 	(dst)->f_frsize	= (src)->f_frsize;			\
61 	(dst)->f_blocks	= (src)->f_blocks;			\
62 	(dst)->f_bfree	= (src)->f_bfree;			\
63 	(dst)->f_bavail	= (src)->f_bavail;			\
64 	(dst)->f_files	= (src)->f_files;			\
65 	(dst)->f_ffree	= (src)->f_ffree;			\
66 	(dst)->f_favail	= (src)->f_favail;			\
67 	(dst)->f_fsid	= (src)->f_fsid;			\
68 	bcopy((src)->f_basetype, (dst)->f_basetype,		\
69 		sizeof ((dst)->f_basetype));			\
70 	(dst)->f_flag	= (src)->f_flag;			\
71 	(dst)->f_namemax = (src)->f_namemax;			\
72 	bcopy((src)->f_fstr, (dst)->f_fstr,			\
73 		sizeof ((dst)->f_fstr))
74 
75 /*
76  * Common routines for statvfs and fstatvfs.
77  */
78 
79 static int
80 cstatvfs32(struct vfs *vfsp, struct statvfs32 *ubp)
81 {
82 	struct statvfs64 ds64;
83 	struct statvfs32 ds32;
84 	int error;
85 
86 #if !defined(lint)
87 	ASSERT32(sizeof (struct statvfs) == sizeof (struct statvfs32));
88 	ASSERT32(sizeof (struct statvfs64) == sizeof (struct statvfs64_32));
89 #endif
90 
91 	bzero(&ds64, sizeof (ds64));
92 	if ((error = VFS_STATVFS(vfsp, &ds64)) != 0)
93 		return (error);
94 
95 	/*
96 	 * VFS_STATVFS can return data that is incompatible with the space
97 	 * available the 32-bit statvfs structure.  Check here to see if
98 	 * it will fit into the 32-bit structure, if not, return EOVERFLOW.
99 	 *
100 	 * The check for -1 is because some file systems return -1 in the
101 	 * fields that are irrelevant or nonessential, and we do not want
102 	 * to return EOVERFLOW for them.  For example: df is expected to
103 	 * show -1 in the output for some of these fields on NFS mounted
104 	 * filesystems.
105 	 */
106 	if (ds64.f_files == (fsfilcnt64_t)-1)
107 		ds64.f_files = UINT32_MAX;
108 	if (ds64.f_ffree == (fsfilcnt64_t)-1)
109 		ds64.f_ffree = UINT32_MAX;
110 	if (ds64.f_favail == (fsfilcnt64_t)-1)
111 		ds64.f_favail = UINT32_MAX;
112 	if (ds64.f_bavail == (fsblkcnt64_t)-1)
113 		ds64.f_bavail = UINT32_MAX;
114 	if (ds64.f_bfree == (fsblkcnt64_t)-1)
115 		ds64.f_bfree = UINT32_MAX;
116 
117 	if (ds64.f_blocks > UINT32_MAX || ds64.f_bfree > UINT32_MAX ||
118 	    ds64.f_bavail > UINT32_MAX || ds64.f_files > UINT32_MAX ||
119 	    ds64.f_ffree > UINT32_MAX || ds64.f_favail > UINT32_MAX)
120 		return (EOVERFLOW);
121 #ifdef _LP64
122 	/*
123 	 * On the 64-bit kernel, even these fields grow to 64-bit
124 	 * quantities in the statvfs64 structure.
125 	 */
126 	if (ds64.f_namemax == (ulong_t)-1l)
127 		ds64.f_namemax = UINT32_MAX;
128 
129 	if (ds64.f_bsize > UINT32_MAX || ds64.f_frsize > UINT32_MAX ||
130 	    ds64.f_fsid > UINT32_MAX || ds64.f_flag > UINT32_MAX ||
131 	    ds64.f_namemax > UINT32_MAX)
132 		return (EOVERFLOW);
133 #endif
134 
135 	bzero(&ds32, sizeof (ds32));
136 	STATVFSCOPY(&ds32, &ds64);
137 	if (copyout(&ds32, ubp, sizeof (ds32)) != 0)
138 		return (EFAULT);
139 	return (0);
140 }
141 
142 static int
143 cstatvfs64(struct vfs *vfsp, struct statvfs64 *ubp)
144 {
145 	struct statvfs64 ds64;
146 	int error;
147 
148 #if !defined(lint)
149 	ASSERT64(sizeof (struct statvfs) == sizeof (struct statvfs64));
150 #endif
151 	bzero(&ds64, sizeof (ds64));
152 	if ((error = VFS_STATVFS(vfsp, &ds64)) != 0)
153 		return (error);
154 	if (copyout(&ds64, ubp, sizeof (ds64)) != 0)
155 		return (EFAULT);
156 	return (0);
157 }
158 
159 /*
160  * Native system calls
161  */
162 int
163 statvfs(char *fname, struct statvfs *sbp)
164 {
165 	vnode_t *vp;
166 	int error;
167 	int estale_retry = 0;
168 
169 lookup:
170 	if (error = lookupname(fname, UIO_USERSPACE, FOLLOW, NULLVPP, &vp)) {
171 		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
172 			goto lookup;
173 		return (set_errno(error));
174 	}
175 #ifdef _LP64
176 	error = cstatvfs64(vp->v_vfsp, (struct statvfs64 *)sbp);
177 #else
178 	error = cstatvfs32(vp->v_vfsp, (struct statvfs32 *)sbp);
179 #endif
180 	VN_RELE(vp);
181 	if (error) {
182 		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
183 			goto lookup;
184 		return (set_errno(error));
185 	}
186 	return (0);
187 }
188 
189 int
190 fstatvfs(int fdes, struct statvfs *sbp)
191 {
192 	struct file *fp;
193 	int error;
194 
195 	if ((fp = getf(fdes)) == NULL)
196 		return (set_errno(EBADF));
197 #ifdef _LP64
198 	error = cstatvfs64(fp->f_vnode->v_vfsp, (struct statvfs64 *)sbp);
199 #else
200 	error = cstatvfs32(fp->f_vnode->v_vfsp, (struct statvfs32 *)sbp);
201 #endif
202 	releasef(fdes);
203 	if (error)
204 		return (set_errno(error));
205 	return (0);
206 }
207 
208 #if defined(_ILP32)
209 
210 /*
211  * Large File system calls.
212  *
213  * (We deliberately don't have special "large file" system calls in the
214  * 64-bit kernel -- we just use the native versions, since they're just
215  * as functional.)
216  */
217 int
218 statvfs64(char *fname, struct statvfs64 *sbp)
219 {
220 	vnode_t *vp;
221 	int error;
222 	int estale_retry = 0;
223 
224 lookup:
225 	if (error = lookupname(fname, UIO_USERSPACE, FOLLOW, NULLVPP, &vp)) {
226 		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
227 			goto lookup;
228 		return (set_errno(error));
229 	}
230 	error = cstatvfs64(vp->v_vfsp, sbp);
231 	VN_RELE(vp);
232 	if (error) {
233 		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
234 			goto lookup;
235 		return (set_errno(error));
236 	}
237 	return (0);
238 }
239 
240 int
241 fstatvfs64(int fdes, struct statvfs64 *sbp)
242 {
243 	struct file *fp;
244 	int error;
245 
246 	if ((fp = getf(fdes)) == NULL)
247 		return (set_errno(EBADF));
248 	error = cstatvfs64(fp->f_vnode->v_vfsp, sbp);
249 	releasef(fdes);
250 	if (error)
251 		return (set_errno(error));
252 	return (0);
253 }
254 
255 #endif	/* _ILP32 */
256 
257 #ifdef _SYSCALL32_IMPL
258 
259 static int
260 cstatvfs64_32(struct vfs *vfsp, struct statvfs64_32 *ubp)
261 {
262 	struct statvfs64 ds64;
263 	struct statvfs64_32 ds64_32;
264 	int error;
265 
266 	bzero(&ds64, sizeof (ds64));
267 	if ((error = VFS_STATVFS(vfsp, &ds64)) != 0)
268 		return (error);
269 
270 	/*
271 	 * On the 64-bit kernel, even these fields grow to 64-bit
272 	 * quantities in the statvfs64 structure.
273 	 */
274 	if (ds64.f_namemax == (ulong_t)-1l)
275 		ds64.f_namemax = UINT32_MAX;
276 
277 	if (ds64.f_bsize > UINT32_MAX || ds64.f_frsize > UINT32_MAX ||
278 	    ds64.f_fsid > UINT32_MAX || ds64.f_flag > UINT32_MAX ||
279 	    ds64.f_namemax > UINT32_MAX)
280 		return (EOVERFLOW);
281 
282 	STATVFSCOPY(&ds64_32, &ds64);
283 	if (copyout(&ds64_32, ubp, sizeof (ds64_32)) != 0)
284 		return (EFAULT);
285 	return (0);
286 }
287 
288 /*
289  * ILP32 "small file" system calls on LP64 kernel
290  */
291 int
292 statvfs32(char *fname, struct statvfs32 *sbp)
293 {
294 	vnode_t *vp;
295 	int error;
296 	int estale_retry = 0;
297 
298 lookup:
299 	if (error = lookupname(fname, UIO_USERSPACE, FOLLOW, NULLVPP, &vp)) {
300 		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
301 			goto lookup;
302 		return (set_errno(error));
303 	}
304 	error = cstatvfs32(vp->v_vfsp, sbp);
305 	VN_RELE(vp);
306 	if (error) {
307 		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
308 			goto lookup;
309 		return (set_errno(error));
310 	}
311 	return (0);
312 }
313 
314 int
315 fstatvfs32(int fdes, struct statvfs32 *sbp)
316 {
317 	struct file *fp;
318 	int error;
319 
320 	if ((fp = getf(fdes)) == NULL)
321 		return (set_errno(EBADF));
322 	error = cstatvfs32(fp->f_vnode->v_vfsp, sbp);
323 	releasef(fdes);
324 	if (error)
325 		return (set_errno(error));
326 	return (0);
327 }
328 
329 /*
330  * ILP32 Large File system calls on LP64 kernel
331  */
332 int
333 statvfs64_32(char *fname, struct statvfs64_32 *sbp)
334 {
335 	vnode_t *vp;
336 	int error;
337 	int estale_retry = 0;
338 
339 lookup:
340 	if (error = lookupname(fname, UIO_USERSPACE, FOLLOW, NULLVPP, &vp)) {
341 		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
342 			goto lookup;
343 		return (set_errno(error));
344 	}
345 	error = cstatvfs64_32(vp->v_vfsp, sbp);
346 	VN_RELE(vp);
347 	if (error) {
348 		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
349 			goto lookup;
350 		return (set_errno(error));
351 	}
352 	return (0);
353 }
354 
355 int
356 fstatvfs64_32(int fdes, struct statvfs64_32 *sbp)
357 {
358 	struct file *fp;
359 	int error;
360 
361 	if ((fp = getf(fdes)) == NULL)
362 		return (set_errno(EBADF));
363 	error = cstatvfs64_32(fp->f_vnode->v_vfsp, sbp);
364 	releasef(fdes);
365 	if (error)
366 		return (set_errno(error));
367 	return (0);
368 }
369 
370 #endif	/* _SYSCALL32_IMPL */
371