root/usr/src/uts/common/syscall/statvfs.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*      Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T     */
/*        All Rights Reserved   */

/*
 * Portions of this source code were derived from Berkeley 4.3 BSD
 * under license from the Regents of the University of California.
 */

/*
 * Get file system statistics (statvfs and fstatvfs).
 */

#include <sys/types.h>
#include <sys/inttypes.h>
#include <sys/t_lock.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/fstyp.h>
#include <sys/systm.h>
#include <sys/vfs.h>
#include <sys/statvfs.h>
#include <sys/vnode.h>
#include <sys/file.h>
#include <sys/cmn_err.h>
#include <sys/debug.h>
#include <sys/pathname.h>

#include <vm/page.h>
#include <fs/fs_subr.h>

#define STATVFSCOPY(dst, src)                                   \
        (dst)->f_bsize  = (src)->f_bsize;                       \
        (dst)->f_frsize = (src)->f_frsize;                      \
        (dst)->f_blocks = (src)->f_blocks;                      \
        (dst)->f_bfree  = (src)->f_bfree;                       \
        (dst)->f_bavail = (src)->f_bavail;                      \
        (dst)->f_files  = (src)->f_files;                       \
        (dst)->f_ffree  = (src)->f_ffree;                       \
        (dst)->f_favail = (src)->f_favail;                      \
        (dst)->f_fsid   = (src)->f_fsid;                        \
        bcopy((src)->f_basetype, (dst)->f_basetype,             \
                sizeof ((dst)->f_basetype));                    \
        (dst)->f_flag   = (src)->f_flag;                        \
        (dst)->f_namemax = (src)->f_namemax;                    \
        bcopy((src)->f_fstr, (dst)->f_fstr,                     \
                sizeof ((dst)->f_fstr))

/*
 * Common routines for statvfs and fstatvfs.
 */

static int
cstatvfs32(struct vfs *vfsp, struct statvfs32 *ubp)
{
        struct statvfs64 ds64;
        struct statvfs32 ds32;
        int error;

#if !defined(lint)
        ASSERT32(sizeof (struct statvfs) == sizeof (struct statvfs32));
        ASSERT32(sizeof (struct statvfs64) == sizeof (struct statvfs64_32));
#endif

        bzero(&ds64, sizeof (ds64));
        if ((error = VFS_STATVFS(vfsp, &ds64)) != 0)
                return (error);

        /*
         * VFS_STATVFS can return data that is incompatible with the space
         * available the 32-bit statvfs structure.  Check here to see if
         * it will fit into the 32-bit structure, if not, return EOVERFLOW.
         *
         * The check for -1 is because some file systems return -1 in the
         * fields that are irrelevant or nonessential, and we do not want
         * to return EOVERFLOW for them.  For example: df is expected to
         * show -1 in the output for some of these fields on NFS mounted
         * filesystems.
         */
        if (ds64.f_files == (fsfilcnt64_t)-1)
                ds64.f_files = UINT32_MAX;
        if (ds64.f_ffree == (fsfilcnt64_t)-1)
                ds64.f_ffree = UINT32_MAX;
        if (ds64.f_favail == (fsfilcnt64_t)-1)
                ds64.f_favail = UINT32_MAX;
        if (ds64.f_bavail == (fsblkcnt64_t)-1)
                ds64.f_bavail = UINT32_MAX;
        if (ds64.f_bfree == (fsblkcnt64_t)-1)
                ds64.f_bfree = UINT32_MAX;

        if (ds64.f_blocks > UINT32_MAX || ds64.f_bfree > UINT32_MAX ||
            ds64.f_bavail > UINT32_MAX || ds64.f_files > UINT32_MAX ||
            ds64.f_ffree > UINT32_MAX || ds64.f_favail > UINT32_MAX)
                return (EOVERFLOW);
#ifdef _LP64
        /*
         * On the 64-bit kernel, even these fields grow to 64-bit
         * quantities in the statvfs64 structure.
         */
        if (ds64.f_namemax == (ulong_t)-1l)
                ds64.f_namemax = UINT32_MAX;

        if (ds64.f_bsize > UINT32_MAX || ds64.f_frsize > UINT32_MAX ||
            ds64.f_fsid > UINT32_MAX || ds64.f_flag > UINT32_MAX ||
            ds64.f_namemax > UINT32_MAX)
                return (EOVERFLOW);
#endif

        bzero(&ds32, sizeof (ds32));
        STATVFSCOPY(&ds32, &ds64);
        if (copyout(&ds32, ubp, sizeof (ds32)) != 0)
                return (EFAULT);
        return (0);
}

static int
cstatvfs64(struct vfs *vfsp, struct statvfs64 *ubp)
{
        struct statvfs64 ds64;
        int error;

#if !defined(lint)
        ASSERT64(sizeof (struct statvfs) == sizeof (struct statvfs64));
#endif
        bzero(&ds64, sizeof (ds64));
        if ((error = VFS_STATVFS(vfsp, &ds64)) != 0)
                return (error);
        if (copyout(&ds64, ubp, sizeof (ds64)) != 0)
                return (EFAULT);
        return (0);
}

/*
 * Native system calls
 */
int
statvfs(char *fname, struct statvfs *sbp)
{
        vnode_t *vp;
        int error;
        int estale_retry = 0;

lookup:
        if (error = lookupname(fname, UIO_USERSPACE, FOLLOW, NULLVPP, &vp)) {
                if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
                        goto lookup;
                return (set_errno(error));
        }
#ifdef _LP64
        error = cstatvfs64(vp->v_vfsp, (struct statvfs64 *)sbp);
#else
        error = cstatvfs32(vp->v_vfsp, (struct statvfs32 *)sbp);
#endif
        VN_RELE(vp);
        if (error) {
                if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
                        goto lookup;
                return (set_errno(error));
        }
        return (0);
}

int
fstatvfs(int fdes, struct statvfs *sbp)
{
        struct file *fp;
        int error;

        if ((fp = getf(fdes)) == NULL)
                return (set_errno(EBADF));
#ifdef _LP64
        error = cstatvfs64(fp->f_vnode->v_vfsp, (struct statvfs64 *)sbp);
#else
        error = cstatvfs32(fp->f_vnode->v_vfsp, (struct statvfs32 *)sbp);
#endif
        releasef(fdes);
        if (error)
                return (set_errno(error));
        return (0);
}

#if defined(_ILP32)

/*
 * Large File system calls.
 *
 * (We deliberately don't have special "large file" system calls in the
 * 64-bit kernel -- we just use the native versions, since they're just
 * as functional.)
 */
int
statvfs64(char *fname, struct statvfs64 *sbp)
{
        vnode_t *vp;
        int error;
        int estale_retry = 0;

lookup:
        if (error = lookupname(fname, UIO_USERSPACE, FOLLOW, NULLVPP, &vp)) {
                if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
                        goto lookup;
                return (set_errno(error));
        }
        error = cstatvfs64(vp->v_vfsp, sbp);
        VN_RELE(vp);
        if (error) {
                if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
                        goto lookup;
                return (set_errno(error));
        }
        return (0);
}

int
fstatvfs64(int fdes, struct statvfs64 *sbp)
{
        struct file *fp;
        int error;

        if ((fp = getf(fdes)) == NULL)
                return (set_errno(EBADF));
        error = cstatvfs64(fp->f_vnode->v_vfsp, sbp);
        releasef(fdes);
        if (error)
                return (set_errno(error));
        return (0);
}

#endif  /* _ILP32 */

#ifdef _SYSCALL32_IMPL

static int
cstatvfs64_32(struct vfs *vfsp, struct statvfs64_32 *ubp)
{
        struct statvfs64 ds64;
        struct statvfs64_32 ds64_32;
        int error;

        bzero(&ds64, sizeof (ds64));
        if ((error = VFS_STATVFS(vfsp, &ds64)) != 0)
                return (error);

        /*
         * On the 64-bit kernel, even these fields grow to 64-bit
         * quantities in the statvfs64 structure.
         */
        if (ds64.f_namemax == (ulong_t)-1l)
                ds64.f_namemax = UINT32_MAX;

        if (ds64.f_bsize > UINT32_MAX || ds64.f_frsize > UINT32_MAX ||
            ds64.f_fsid > UINT32_MAX || ds64.f_flag > UINT32_MAX ||
            ds64.f_namemax > UINT32_MAX)
                return (EOVERFLOW);

        STATVFSCOPY(&ds64_32, &ds64);
        if (copyout(&ds64_32, ubp, sizeof (ds64_32)) != 0)
                return (EFAULT);
        return (0);
}

/*
 * ILP32 "small file" system calls on LP64 kernel
 */
int
statvfs32(char *fname, struct statvfs32 *sbp)
{
        vnode_t *vp;
        int error;
        int estale_retry = 0;

lookup:
        if (error = lookupname(fname, UIO_USERSPACE, FOLLOW, NULLVPP, &vp)) {
                if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
                        goto lookup;
                return (set_errno(error));
        }
        error = cstatvfs32(vp->v_vfsp, sbp);
        VN_RELE(vp);
        if (error) {
                if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
                        goto lookup;
                return (set_errno(error));
        }
        return (0);
}

int
fstatvfs32(int fdes, struct statvfs32 *sbp)
{
        struct file *fp;
        int error;

        if ((fp = getf(fdes)) == NULL)
                return (set_errno(EBADF));
        error = cstatvfs32(fp->f_vnode->v_vfsp, sbp);
        releasef(fdes);
        if (error)
                return (set_errno(error));
        return (0);
}

/*
 * ILP32 Large File system calls on LP64 kernel
 */
int
statvfs64_32(char *fname, struct statvfs64_32 *sbp)
{
        vnode_t *vp;
        int error;
        int estale_retry = 0;

lookup:
        if (error = lookupname(fname, UIO_USERSPACE, FOLLOW, NULLVPP, &vp)) {
                if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
                        goto lookup;
                return (set_errno(error));
        }
        error = cstatvfs64_32(vp->v_vfsp, sbp);
        VN_RELE(vp);
        if (error) {
                if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
                        goto lookup;
                return (set_errno(error));
        }
        return (0);
}

int
fstatvfs64_32(int fdes, struct statvfs64_32 *sbp)
{
        struct file *fp;
        int error;

        if ((fp = getf(fdes)) == NULL)
                return (set_errno(EBADF));
        error = cstatvfs64_32(fp->f_vnode->v_vfsp, sbp);
        releasef(fdes);
        if (error)
                return (set_errno(error));
        return (0);
}

#endif  /* _SYSCALL32_IMPL */