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
79static int
80cstatvfs32(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
142static int
143cstatvfs64(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 */
162int
163statvfs(char *fname, struct statvfs *sbp)
164{
165	vnode_t *vp;
166	int error;
167	int estale_retry = 0;
168
169lookup:
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
189int
190fstatvfs(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 */
217int
218statvfs64(char *fname, struct statvfs64 *sbp)
219{
220	vnode_t *vp;
221	int error;
222	int estale_retry = 0;
223
224lookup:
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
240int
241fstatvfs64(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
259static int
260cstatvfs64_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 */
291int
292statvfs32(char *fname, struct statvfs32 *sbp)
293{
294	vnode_t *vp;
295	int error;
296	int estale_retry = 0;
297
298lookup:
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
314int
315fstatvfs32(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 */
332int
333statvfs64_32(char *fname, struct statvfs64_32 *sbp)
334{
335	vnode_t *vp;
336	int error;
337	int estale_retry = 0;
338
339lookup:
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
355int
356fstatvfs64_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