mntvnops.c revision a51518b114948c25adf55d3b4393ccc00ba2b5b8
/*
* 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
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#include <sys/vfs_opreg.h>
#define MNTROOTINO 2
extern struct vnode *mntdummyvp;
/*
* Design of kernel mnttab accounting.
*
* To support whitespace in mount names, we implement an ioctl
* (MNTIOC_GETMNTENT) which allows a programmatic interface to the data in
* atop this interface.
*
* To minimize the amount of memory used in the kernel, we keep all the
* necessary information in the user's address space. Large server
*
* To support both vanilla read() calls as well as ioctl() calls, we have two
* different snapshots of the kernel data structures, mnt_read and mnt_ioctl.
* These snapshots include the base location in user memory, the number of
* mounts in the snapshot, and any metadata associated with it. The metadata is
* used only to support the ioctl() interface, and is a series of extmnttab
* structures. When the user issues an ioctl(), we simply copyout a pointer to
* that structure, and the rest is handled in userland.
*/
/*
* NOTE: The following variable enables the generation of the "dev=xxx"
* in the option string for a mounted file system. Really this should
* be gotten rid of altogether, but for the sake of backwards compatibility
* we had to leave it in. It is defined as a 32-bit device number. This
* means that when 64-bit device numbers are in use, if either the major or
* minor part of the device number will not fit in a 16 bit quantity, the
* "dev=" will be set to NODEV (0x7fffffff). See PSARC 1999/566 and
* 1999/131 for details. The cmpldev() function used to generate the 32-bit
* device number handles this check and assigns the proper value.
*/
static int
{
}
static int
{
}
static int
{
int i, size = 0;
continue;
if (size)
size++; /* space for comma */
/*
* count option value if there is one
*/
}
}
}
/*
* Add space for "zone=<zone_name>" if required.
*/
if (size)
size++; /* space for comma */
}
if (mntfs_enabledev) {
if (size != 0)
size++; /* space for comma */
}
if (size == 0)
return (size);
}
static int
{
int i, optinbuf = 0;
continue;
if (optinbuf)
*buf++ = ',';
else
optinbuf = 1;
/*
* print option value if there is one
*/
}
}
}
if (optinbuf)
*buf++ = ',';
else
optinbuf = 1;
}
if (mntfs_enabledev) {
if (optinbuf++)
*buf++ = ',';
}
if (!optinbuf) {
}
}
static size_t
{
} else {
}
if (resource[0] != '/') {
/*
* Same as the zone's view of the mount point.
*/
} else {
}
} else {
}
return (size);
}
static void
{
/*
* Basically copy over the real vfs_t on which the root vnode is
* located, changing its mountpoint and resource to match those of
* the zone's rootpath.
*/
}
static size_t
{
/*
* If the zone has a root entry, it will be the first in the list. If
* it doesn't, we conjure one up.
*/
/*
* The root of the zone is not a mount point. The vfs we want
* to report is that of the zone's root vnode.
*/
cnt++;
}
goto out;
do {
/*
* Skip mounts that should not show up in mnttab
*/
continue;
}
cnt++;
out:
return (size);
}
static size_t
{
do {
/*
* Skip mounts that should not show up in mnttab
*/
continue;
}
cnt++;
return (size);
}
static void
{
if (tab)
if (resource[0] != '/') {
/*
* Use the mount point as the resource.
*/
} else {
}
} else {
}
if (tab)
/*
* We know the mount point is visible from within the zone,
* otherwise it wouldn't be on the zone's vfs list.
*/
} else {
}
if (tab)
if (tab)
if (tab)
if (tab) {
}
}
static void
{
/*
* If the zone has a root entry, it will be the first in the list. If
* it doesn't, we conjure one up.
*/
/*
* The root of the zone is not a mount point. The vfs we want
* to report is that of the zone's root vnode.
*/
if (tab)
tab++;
}
return;
do {
/*
* Skip mounts that should not show up in mnttab
*/
continue;
}
if (tab)
tab++;
}
static void
int forread)
{
do {
/*
* Skip mounts that should not show up in mnttab
*/
continue;
}
if (tab)
tab++;
}
static char *
{
char *addr;
return (NULL);
}
return (NULL);
}
return (addr);
}
static void
{
snap->mnts_metasize = 0;
}
#ifdef _SYSCALL32_IMPL
typedef struct extmnttab32 {
#endif
/*
* called to generate a dummy read vop call so that
*/
static void
{
char tbuf[1];
if (mntdummyvp == NULL)
return;
/*
* Make a VOP_READ call on the dummy vnode so that any
* module interested in mnttab getting modified could
* intercept this vnode and capture the event.
*
* Pass a dummy uio struct. Nobody should reference the buffer.
* We need to pass a valid uio struct pointer to take care of
* any module intercepting this vnode which could attempt to
* look at it. Currently only the file events notification
* module intercepts this vnode.
*/
uio.uio_loffset = 0;
}
/*
* Snapshot the latest version of the kernel mounted resource information
*
* There are two types of snapshots: one destined for reading, and one destined
* for ioctl(). The difference is that the ioctl() interface is delimited by
* NULLs, while the read() interface is delimited by tabs and newlines.
*/
/* ARGSUSED */
static int
{
struct extmnttab *metadata_baseaddr;
char *text_baseaddr;
int i;
if (forread)
else
/*
* Check if the mnttab info has changed since the last snapshot
*/
if (snap->mnts_count &&
return (0);
}
if (snap->mnts_count != 0)
if (global_view)
else
if (!forread)
else
if (global_view)
else
/*
* The pointers in the metadata refer to addreesses in the range
* [base_addr, base_addr + size]. Now that we have mapped the text into
* the user's address space, we have to convert these addresses into the
* new (user) range. We also handle the conversion for 32-bit and
* 32-bit applications here.
*/
if (!forread) {
#ifdef _SYSCALL32_IMPL
struct extmnttab32 *tab32;
if (datamodel == DATAMODEL_ILP32) {
for (i = 0; i < snap->mnts_count; i++) {
tab32[i].mnt_special =
tab32[i].mnt_mountp =
tab32[i].mnt_fstype =
tab32[i].mnt_mntopts =
}
(char *)metadata_baseaddr,
} else {
#endif
for (i = 0; i < snap->mnts_count; i++) {
}
#ifdef _SYSCALL32_IMPL
}
#endif
}
return (ENOMEM);
}
return (0);
}
/*
* Public function to convert vfs_mntopts into a string.
* A buffer of sufficient size is allocated, which is returned via bufp,
* and whose length is returned via lenp.
*/
void
{
char *buf;
return;
}
}
/* ARGSUSED */
static int
{
/*
* Not allowed to open for writing, return error.
*/
return (EPERM);
/*
* hang the snapshot on.
*/
return (0);
}
/* ARGSUSED */
static int
{
/* Clean up any locks or shares held by the current process */
if (count > 1)
return (0);
}
return (0);
}
/* ARGSUSED */
static int
{
int error = 0;
char *buf;
int datamodel;
/*
* It is assumed that any kernel callers wishing
* to read mnttab will be using extmnttab entries
* and not extmnttab32 entries, whether or not
* the kernel is LP64 or ILP32. Thus, force the
* datamodel that mntfs_snapshot uses to be
* DATAMODEL_LP64.
*/
else
datamodel = get_udatamodel();
return (error);
}
return (EFAULT);
if (len == 0)
return (0);
/*
* The mnttab image is stored in the user's address space,
* so we have to copy it into the kernel from userland,
* then copy it back out to the specified address.
*/
else {
}
return (error);
}
static int
{
int error;
extern timespec_t vfs_mnttab_ctime;
/*
* Return all the attributes. Should be refined
* so that it returns only those asked for.
* Most of this is complete fakery anyway.
*/
/*
* Attributes are same as underlying file with modifications
*/
return (error);
/*
* We always look like a regular file
*/
/*
* mode should basically be read only
*/
/*
* Set nlink to the number of open vnodes for mnttab info
* plus one for existing.
*/
/*
* If we haven't taken a snapshot yet, set the
* size to the size of the latest snapshot.
*/
/*
* Fetch mtime from the vfs mnttab timestamp
*/
/*
* Nodeid is always ROOTINO;
*/
return (0);
}
static int
{
return (EROFS);
/*
* Do access check on the underlying directory vnode.
*/
}
/*
* New /mntfs vnode required; allocate it and fill in most of the fields.
*/
static mntnode_t *
{
return (mnp);
}
/*
* Free the storage obtained from mntgetnode().
*/
static void
{
vn_invalid(vp);
}
/* ARGSUSED */
static int
{
return (0);
}
/* ARGSUSED */
static void
{
}
/* ARGSUSED */
static int
{
if (*noffp == 0)
return (0);
}
/*
* Return the answer requested to poll().
* POLLRDBAND will return when the mtime of the mnttab
* information is newer than the latest one read for this open.
*/
/* ARGSUSED */
static int
{
*revp = 0;
if (ev & POLLRDNORM)
*revp |= POLLRDNORM;
if (ev & POLLRDBAND) {
*revp |= POLLRDBAND;
}
return (0);
}
/*
* If someone is polling an unsupported poll events (e.g.
* POLLOUT, POLLPRI, etc.), just return POLLERR revents.
* That way we will ensure that we don't return a 0
* revents with a NULL pollhead pointer.
*/
return (0);
}
/* ARGSUSED */
static int
{
int error;
error = 0;
switch (cmd) {
case MNTIOC_NMNTS: { /* get no. of mounted resources */
if (snap->mnts_count == 0) {
if ((error =
return (error);
}
break;
}
case MNTIOC_GETDEVLIST: { /* get mounted device major/minor nos */
int i;
if (snap->mnts_count == 0) {
if ((error =
return (error);
}
for (i = 0; i < snap->mnts_count; i++) {
#ifdef _SYSCALL32_IMPL
struct extmnttab32 tab;
i * sizeof (struct extmnttab32), &tab,
sizeof (tab))) != 0)
break;
} else {
#endif
sizeof (tab))) != 0)
break;
#ifdef _SYSCALL32_IMPL
}
#endif
}
if (error == 0)
break;
}
case MNTIOC_SETTAG: /* set tag on mounted file system */
case MNTIOC_CLRTAG: /* clear tag on mounted file system */
{
char *cptr;
char tagbuf[MAX_MNTOPT_TAG];
char *pbuf;
break;
}
if (zone != global_zone) {
/* truncate "/" and nul */
}
if (error) {
break;
}
break;
}
break;
}
if (cmd == MNTIOC_SETTAG)
else
break;
}
case MNTIOC_SHOWHIDDEN:
{
break;
}
case MNTIOC_GETMNTENT:
{
if ((error =
return (error);
}
/*
* If the next index is beyond the end of the current mnttab,
* return EOF
*/
*rvalp = 1;
return (0);
}
#ifdef _SYSCALL32_IMPL
sizeof (struct extmnttab32));
} else {
#endif
sizeof (struct extmnttab));
#ifdef _SYSCALL32_IMPL
}
#endif
if (error != 0)
return (error);
mnp->mnt_offset++;
break;
}
default:
break;
}
return (error);
}
/* ARGSUSED */
static int
{
return (0);
}
/* ARGSUSED */
static int
{
return (0);
}
/*
* /mntfs vnode operations vector
*/
const fs_operation_def_t mnt_vnodeops_template[] = {
};
const fs_operation_def_t mnt_dummyvnodeops_template[] = {
};