vfs.c revision d320ee2ded329e5f18dd4cf6a0de7c592a65841b
/*
* 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.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/vfs_opreg.h>
#include <sys/rwstlock.h>
#include <sys/pathname.h>
#include <sys/bootconf.h>
#include <sys/sysmacros.h>
/* Private interfaces to create vopstats-related data structures */
extern void initialize_vopstats(vopstats_t *);
static void vfs_clearmntopt_nolock(mntopts_t *, const char *, int);
static void vfs_setmntopt_nolock(mntopts_t *, const char *,
const char *, int, int);
static int vfs_optionisset_nolock(const mntopts_t *, const char *, char **);
static void vfs_freemnttab(struct vfs *);
static void vfs_freeopt(mntopt_t *);
static void vfs_createopttbl_extend(mntopts_t *, const char *,
const mntopts_t *);
static char **vfs_copycancelopt_extend(char **const, int);
static void vfs_freecancelopt(char **);
static char *getrootfs(void);
static int getmacpath(dev_info_t *, void *);
struct ipmnt {
};
static kmutex_t vfs_miplist_mutex;
/*
* VFS global data.
*/
char *server_rootpath; /* root path for diskless clients */
char *server_hostname; /* hostname of diskless server */
/* must be power of 2! */
char *vfs_dummyfstype = "\0";
/*
* Table for generic options recognized in the VFS layer and acted
* on at this level before parsing file system specific options.
* The nosuid option is stronger than any of the devices and setuid
* options, so those are canceled when nosuid is seen.
*
* All options which are added here need to be added to the
*/
/*
* VFS Mount options table
*/
/*
* option name cancel options default arg flags
*/
MO_NODISPLAY, (void *)0 },
(void *)0 },
(void *)0 },
(void *)0 },
(void *)0 },
(void *)0 },
(void *)0 },
(void *)0 },
(void *)0 },
(void *)0 },
(void *)0 },
(void *)0 },
(void *)0 },
};
const mntopts_t vfs_mntopts = {
};
/*
* File system operation dispatch functions.
*/
int
{
}
int
{
}
int
{
/*
* Make sure this root has a path. With lofs, it is possible to have
* a NULL mountpoint.
*/
}
return (ret);
}
int
{
}
int
{
}
int
{
}
int
{
}
void
{
}
int
{
}
int
{
else
return (ENOTSUP);
}
/*
* File system initialization. vfs_setfsops() must be called from a file
* system's init routine.
*/
static int
int *unused_ops)
{
static const fs_operation_trans_def_t vfs_ops_table[] = {
};
}
int
{
int error;
int unused_ops;
/*
* Verify that fstype refers to a valid fs. Note that
* 0 is valid since it's used to set "stray" ops.
*/
return (EINVAL);
return (EINVAL);
/* Set up the operations vector. */
if (error != 0)
return (error);
#if DEBUG
if (unused_ops != 0)
#endif
return (0);
}
int
{
int error;
int unused_ops;
if (error != 0) {
return (error);
}
return (0);
}
/*
* Free a vfsops structure created as a result of vfs_makefsops().
* NOTE: For a vfsops structure initialized by vfs_setfsops(), use
* vfs_freevfsops_by_type().
*/
void
{
}
/*
* Since the vfsops structure is part of the vfssw table and wasn't
* really allocated, we're not really freeing anything. We keep
* the name for consistency with vfs_freevfsops(). We do, however,
* need to take care of a little bookkeeping.
* NOTE: For a vfsops structure created by vfs_setfsops(), use
* vfs_freevfsops_by_type().
*/
int
{
/* Verify that fstype refers to a loaded fs (and not fsid 0). */
return (EINVAL);
WLOCK_VFSSW();
return (EINVAL);
}
return (0);
}
/* Support routines used to reference vfs_op */
/* Set the operations vector for a vfs */
void
{
return;
}
}
/* Retrieve the operations vector for a vfs */
vfsops_t *
{
return (op);
} else {
return (fsem_getvfsops(vfsp));
}
}
/*
* Returns non-zero (1) if the vfsops matches that of the vfs.
* Returns zero (0) if not.
*/
int
{
}
/*
* Returns non-zero (1) if the file system has installed a non-default,
* non-error vfs_sync routine. Returns zero (0) otherwise.
*/
int
{
}
/*
* Initialize a vfs structure.
*/
void
{
/*
* Note: Don't initialize any member of the vfs_impl_t structure
* here as it could be a problem for unbundled file systems.
*/
}
/*
* Allocate and initialize the vfs implementation private data
* structure, vfs_impl_t.
*/
void
{
/* Note that this are #define'd in vfs.h */
}
/*
* Release the vfs_impl_t structure, if it exists. Some unbundled
* filesystems may not use the newer version of vfs and thus
* would not contain this implementation private data structure.
*/
void
{
return;
if (vip->vi_femhead) {
}
}
/*
* VFS system calls: mount, umount, syssync, statfs, fstatfs, statvfs,
*/
/*
* Update every mounted file system. We call the vfs_sync operation of
* each file system type, passing it a NULL vfsp to indicate that all
* mounted file systems of that type should be updated.
*/
void
{
RLOCK_VFSSW();
CRED());
RLOCK_VFSSW();
}
}
}
void
sync(void)
{
vfs_sync(0);
}
/*
* External routines.
*/
/*
* Lock for accessing the vfs linked list. Initialized in vfs_mountroot(),
* but otherwise should be accessed only via vfs_list_lock() and
* vfs_list_unlock(). Also used to protect the timestamp for mods to the list.
*/
/*
* Mount devfs on /devices. This is done right after root is mounted
* to provide device access support for the system
*/
static void
vfs_mountdevices(void)
{
NULL,
NULL,
NULL,
NULL,
0,
NULL,
0
};
/*
* _init devfs module to fill in the vfssw
*/
panic("Cannot _init devfs module");
/*
* Hold vfs
*/
RLOCK_VFSSW();
/*
* Locate mount point
*/
panic("Cannot find /devices");
/*
* Perform the mount of /devices
*/
panic("Cannot mount /devices");
/*
* Set appropriate members and add to vfs list for mnttab display
*/
/*
* Hold the root of /devices so it won't go away
*/
panic("vfs_mountdevices: not devices root");
return;
}
if (vn_vfswlock(mvp) != 0) {
return;
}
}
/*
* mount the first instance of /dev to root and remain mounted
*/
static void
vfs_mountdev1(void)
{
NULL,
NULL,
NULL,
NULL,
0,
NULL,
0
};
/*
* _init dev module to fill in the vfssw
*/
/*
* Hold vfs
*/
RLOCK_VFSSW();
/*
* Locate mount point
*/
/*
* Perform the mount of /dev
*/
/*
* Set appropriate members and add to vfs list for mnttab display
*/
/*
* Hold the root of /dev so it won't go away
*/
return;
}
if (vn_vfswlock(mvp) != 0) {
vfs_unlock(&dev);
return;
}
vfs_unlock(&dev);
}
/*
* Mount required filesystem. This is done right after root is mounted.
*/
static void
{
return;
}
else
}
/*
* vfs_mountroot is called by main() to mount the root filesystem.
*/
void
vfs_mountroot(void)
{
char *path;
/*
* Alloc the vfs hash bucket array and locks
*/
/*
* Call machine-dependent routine "rootconf" to choose a root
* file system type.
*/
if (rootconf())
panic("vfs_mountroot: cannot mount root");
/*
* Get vnode for '/'. Set up rootdir, u.u_rdir and u.u_cdir
* to point to it. These are used by lookuppn() so that it
* knows where to start from ('/' or '.').
*/
panic("vfs_mountroot: no root vnode");
/*
* Setup the global zone's rootvp, now that it exists.
*/
/*
* Notify the module code that it can begin using the
* root filesystem instead of the boot program's services.
*/
modrootloaded = 1;
/*
* Set up mnttab information for root
*/
/*
* Notify cluster software that the root filesystem is available.
*/
/* Now that we're all done with the root FS, set up its vopstats */
/* Set flag for statistics collection */
}
}
/*
*/
#ifdef __sparc
/*
* This bit of magic can go away when we convert sparc to
* the new boot architecture based on ramdisk.
*
* Booting off a mirrored root volume:
* At this point, we have booted and mounted root on a
* single component of the mirror. Complete the boot
* by configuring SVM and converting the root to the
* dev_t of the mirrored root device. This dev_t conversion
* only works because the underlying device doesn't change.
*/
if (root_is_svm) {
if (svm_rootconf()) {
panic("vfs_mountroot: cannot remount root");
}
/*
* mnttab should reflect the new root device
*/
}
#endif /* __sparc */
/*
* Look up the root device via devfs so that a dv_node is
* created for it. The vnode is never VN_RELE()ed.
* We allocate more than MAXPATHLEN so that the
* buffer passed to i_ddi_prompath_to_devfspath() is
* exactly MAXPATHLEN (the function expects a buffer
* of that length).
*/
!= DDI_SUCCESS ||
/* NUL terminate in case "path" has garbage */
#ifdef DEBUG
#endif
}
}
/*
* If remount failed and we're in a zone we need to check for the zone
* root path and strip it before the call to vfs_setpath().
*
* If strpath doesn't begin with the zone_rootpath the original
* strpath is returned unchanged.
*/
static const char *
stripzonepath(const char *strpath)
{
int i;
return (NULL);
}
/*
* we check for the end of the string at one past the
* current position because the zone_rootpath always
* ends with "/" but we don't want to strip that off.
*/
return ((char *)strpath);
}
return (&str2[i]);
}
/*
* Common mount code. Called from the system call entry point, from autofs,
* and from pxfs.
*
* Takes the effective file system type, mount arguments, the mount point
* vnode, flags specifying whether the mount is a remount and whether it
* should be entered into the vfs list, and credentials. Fills in its vfspp
* parameter with the mounted file system instance's vfs.
*
* Note that the effective file system type is specified as a string. It may
* be null, in which case it's determined from the mount arguments, and may
* differ from the type specified in the mount arguments; this is a hook to
* allow interposition when instantiating file system instances.
*
* The caller is responsible for releasing its own hold on the mount point
* vp (this routine does its own hold when necessary).
* Also note that for remounts, the mount point vp should be the vnode for
* the root of the file system rather than the vnode that the file system
* is mounted on top of.
*/
int
{
int error = 0;
int copyout_error = 0;
int ovflags;
int remount;
int rdonly;
int nbmand = 0;
int delmip = 0;
int addmip = 0;
/*
* The v_flag value for the mount point vp is permanently set
* to VVFSLOCK so that no one bypasses the vn_vfs*locks routine
* for mount point locking.
*/
mnt_mntopts.mo_count = 0;
/*
* Find the ops vector to use to invoke the file system-specific mount
* method. If the fsname argument is non-NULL, use it directly.
* Otherwise, dig the file system type information out of the mount
* arguments.
*
* A side effect is to hold the vfssw entry.
*
* Mount arguments can be specified in several ways, which are
* distinguished by flag bit settings. The preferred way is to set
* MS_OPTIONSTR, indicating an 8 argument mount with the file system
* type supplied as a character string and the last two arguments
* being a pointer to a character buffer and the size of the buffer.
* On entry, the buffer holds a null terminated list of options; on
* return, the string is the list of options the file system
* recognized. If MS_DATA is set arguments five and six point to a
* block of binary data which the file system interprets.
* A further wrinkle is that some callers don't set MS_FSS and MS_DATA
* consistently with these conventions. To handle them, we check to
* see whether the pointer to the file system name has a numeric value
* less than 256. If so, we treat it as an index.
*/
return (EINVAL);
}
size_t n;
RLOCK_VFSSW();
return (EINVAL);
}
return (EINVAL);
} else {
/*
* Handle either kernel or user address space.
*/
FSTYPSZ, &n);
} else {
FSTYPSZ, &n);
}
if (error) {
if (error == ENAMETOOLONG)
return (EINVAL);
return (error);
}
return (EINVAL);
}
} else {
return (EINVAL);
}
if (!VFS_INSTALLED(vswp))
return (EINVAL);
/*
* Fetch mount options and parse them for generic vfs options
*/
/*
* Limit the buffer size
*/
goto errout;
}
inargs[0] = '\0';
if (optlen) {
NULL);
if (error) {
goto errout;
}
}
}
}
/*
* Flag bits override the options string.
*/
/*
* Check if this is a remount; must be set in the option string and
* the file system must support a remount option.
*/
MNTOPT_REMOUNT, NULL)) {
goto errout;
}
}
/*
* uap->flags and vfs_optionisset() should agree.
*/
}
}
/*
* If we are splicing the fs into the namespace,
* perform mount point checks.
*
* We want to resolve the path for the mount point to eliminate
* '.' and ".." and symlinks in mount points; we can't do the
* same for the resource string, since it would turn
* this before grabbing vn_vfswlock(), because otherwise we
* would deadlock with lookuppn().
*/
if (splice) {
/*
* Pick up mount point and device from appropriate space.
*/
KM_SLEEP);
}
/*
* Do a lookupname prior to taking the
* writelock. Mark this as completed if
* successful for later cleanup and addition to
* the mount in progress table.
*/
addmip = 1;
}
goto errout;
}
/*
* Kludge to prevent autofs from deadlocking with
* itself when it calls domount().
*
* If autofs is calling, it is because it is doing
* (autofs) mounts in the process of an NFS mount. A
* lookuppn() here would cause us to block waiting for
* said NFS mount to complete, which can't since this
* is the thread that was supposed to doing it.
*/
if (fromspace == UIO_USERSPACE) {
NULL)) == 0) {
} else {
/*
* The file disappeared or otherwise
* became inaccessible since we opened
* it; might as well fail the mount
* since the mount point is no longer
* accessible.
*/
goto errout;
}
} else {
}
/*
* If the addition of the zone's rootpath
* would push us over a total path length
* of MAXPATHLEN, we fail the mount with
* ENAMETOOLONG, which is what we would have
* gotten if we were trying to perform the same
* mount in the global zone.
*
* strlen() doesn't count the trailing
* '\0', but zone_rootpathlen counts both a
* trailing '/' and the terminating '\0'.
*/
}
}
if (error)
goto errout;
/*
* Prevent path name resolution from proceeding past
* the mount point.
*/
if (vn_vfswlock(vp) != 0) {
goto errout;
}
/*
* Verify that it's legitimate to establish a mount on
* the prospective mount point.
*/
/*
* The mount point lock was obtained after some
* other thread raced through and established a mount.
*/
goto errout;
}
goto errout;
}
}
}
/*
* If this is a remount, we don't want to create a new VFS.
* Instead, we pass the existing one with a remount flag.
*/
if (remount) {
/*
* Confirm that the mount point is the root vnode of the
* file system that is being remounted.
* This can happen if the user specifies a different
* mount point directory pathname in the (re)mount command.
*
* Code below can only be reached if splice is true, so it's
* safe to do vn_vfsunlock() here.
*/
goto errout;
}
/*
* Disallow making file systems read-only unless file system
* explicitly allows it in its vfssw. Ignore other flags.
*/
goto errout;
}
/*
* Changing the NBMAND setting on remounts is permitted
* but logged since it can lead to unexpected behavior.
* We also counsel against using it for / and /usr.
*/
}
} else {
}
/*
* The vfs_reflock is not used anymore the code below explicitly
* holds it preventing others accesing it directly.
*/
/*
* Lock the vfs. If this is a remount we want to avoid spurious umount
* failures that happen as a side-effect of fsflush() and other mount
* and unmount operations that might be going on simultaneously and
* may have locked the vfs currently. To not return EBUSY immediately
* here we use vfs_lock_wait() instead vfs_lock() for the remount case.
*/
if (!remount) {
if (splice)
goto errout;
}
} else {
}
/*
* Add device to mount in progress table, global mounts require special
* handling. It is possible that we have already done the lookupname
* on a spliced, non-global fs. If so, we don't want to do it again
* since we cannot do a lookupname after taking the
* wlock above. This case is for a non-spliced, non-global filesystem.
*/
if (!addmip) {
addmip = 1;
}
}
if (addmip) {
addmip = 0;
delmip = 1;
}
/*
* Invalidate cached entry for the mount point.
*/
if (splice)
/*
* If have an option string but the filesystem doesn't supply a
* prototype options table, create a table with the global
* options and sufficient room to accept all the options in the
* string. Then parse the passed in option string
* accepting all the options in the string. This gives us an
* option table with all the proper cancel properties for the
* global options.
*
* Filesystems that supply a prototype options table are handled
* earlier in this function.
*/
tmp_mntopts.mo_count = 0;
&mnt_mntopts);
}
}
/*
* Serialize with zone creations.
*/
/*
* Instantiate (or reinstantiate) the file system. If appropriate,
* splice it into the file system name space.
*
* We want VFS_MOUNT() to be able to override the vfs_resource
* string if necessary (ie, mntfs), and also for a remount to
* change the same (necessary when remounting '/' during boot).
* So we set up vfs_mntpt and vfs_resource to what we think they
* should be, then hand off control to VFS_MOUNT() which can
* override this.
*
* For safety's sake, when changing vfs_resource or vfs_mntpt of
* a vfs which is on the vfs list (i.e. during a remount), we must
* never set those fields to NULL. Several bits of code make
* assumptions that the fields are always valid.
*/
if (remount) {
}
if (error) {
if (remount) {
/* put back pre-remount options */
refstr_value(oldmntpt))));
if (oldmntpt)
refstr_value(oldresource))));
if (oldresource)
} else {
}
} else {
/*
* Set the mount time to now
*/
if (remount) {
if (oldresource)
if (oldmntpt)
} else if (splice) {
/*
* Link vfsp into the name space at the mount
* point. Vfs_add() is responsible for
* holding the mount point which will be
* released when vfs_remove() is called.
*/
} else {
/*
* Hold the reference to file system which is
* not linked into the name space.
*/
}
/*
* Set flags for global options encountered
*/
else
} else {
else
else
}
else
else
else
/*
* Now construct the output option string of options
* we recognized.
*/
if (copyout_error == 0 &&
}
}
/*
* If this isn't a remount, set up the vopstats before
* anyone can touch this. We only allow spliced file
* systems (file systems which are in the namespace) to
* have the VFS_STATS flag set.
* NOTE: PxFS mounts the underlying file system with
* MS_NOSPLICE set and copies those vfs_flags to its private
* vfs structure. As a result, PxFS should never have
* the VFS_STATS flag or else we might access the vfs
* statistics-related fields prior to them being
* properly initialized.
*/
/*
* We need to set vfs_vskap to NULL because there's
* a chance it won't be set below. This is checked
* in teardown_vopstats() so we can't have garbage.
*/
}
}
if (splice)
if ((error == 0) && (copyout_error == 0)) {
if (!remount) {
/*
* Don't call get_vskstat_anchor() while holding
* locks since it allocates memory and calls
* VFS_STATVFS(). For NFS, the latter can generate
* an over-the-wire call.
*/
/* Only take the lock if we have something to do */
}
}
}
/* Return vfsp to caller. */
}
/*
* It is possible we errored prior to adding to mount in progress
* table. Must free vnode we acquired with successful lookupname.
*/
if (addmip)
if (delmip)
if (copyout_error) {
}
return (error);
}
static void
{
char *sp;
int have_list_lock = 0;
/*
* New path must be less than MAXPATHLEN because mntfs
* will only display up to MAXPATHLEN bytes. This is currently
* safe, because domount() uses pn_get(), and other callers
* similarly cap the size to fewer than MAXPATHLEN bytes.
*/
/* mntfs requires consistency while vfs list lock is held */
if (VFS_ON_LIST(vfsp)) {
have_list_lock = 1;
}
refstr_rele(*refp);
/* Do we need to modify the path? */
goto out;
}
/*
* Truncate the trailing '/' in the zoneroot, and merge
* in the zone's rootpath with the "newpath" (resource
* or mountpoint) passed in.
*
* The size of the required buffer is thus the size of
* the buffer required for the passed-in newpath
* (strlen(newpath) + 1), plus the size of the buffer
* required to hold zone_rootpath (zone_rootpathlen)
* minus one for one of the now-superfluous NUL
* terminations, minus one for the trailing '/'.
*
* That gives us:
*
* (strlen(newpath) + 1) + zone_rootpathlen - 1 - 1
*
* Which is what we have below.
*/
/*
* Copy everything including the trailing slash, which
* we then overwrite with the NUL character.
*/
out:
if (have_list_lock) {
}
}
/*
* Record a mounted resource name in a vfs structure.
* If vfsp is already mounted, caller must hold the vfs lock.
*/
void
{
}
/*
* Record a mount point name in a vfs structure.
* If vfsp is already mounted, caller must hold the vfs lock.
*/
void
{
mntpt = VFS_NOMNTPT;
}
/* Returns the vfs_resource. Caller must call refstr_rele() when finished. */
refstr_t *
{
return (resource);
}
/* Returns the vfs_mntpt. Caller must call refstr_rele() when finished. */
refstr_t *
{
return (mntpt);
}
/*
* Create an empty options table with enough empty slots to hold all
* The options in the options string passed as an argument.
* Potentially prepend another options table.
*
* Note: caller is responsible for locking the vfs list, if needed,
* to protect mops.
*/
static void
{
const char *s = opts;
count = 0;
} else {
count = 1;
/*
* Count number of options in the string
*/
count++;
s++;
}
}
}
/*
* Create an empty options table with enough empty slots to hold all
* The options in the options string passed as an argument.
*
* This function is *not* for general use by filesystems.
*
* Note: caller is responsible for locking the vfs list, if needed,
* to protect mops.
*/
void
{
}
/*
* Swap two mount options tables
*/
static void
{
}
static void
{
}
static char **
{
int i = 0;
int j;
char **result;
/* count number of options to cancel */;
}
if (i + extend == 0)
return (NULL);
for (j = 0; j < i; j++) {
}
for (; j <= i + extend; j++)
return (result);
}
static void
{
} else {
}
} else {
}
}
/*
* Copy a mount options table, possibly allocating some spare
* slots at the end. It is permissible to copy_extend the NULL table.
*/
static void
{
/*
* Clear out any existing stuff in the options table being initialized
*/
return;
for (i = 0; i < count; i++) {
}
}
}
/*
* Copy a mount options table.
*
* This function is *not* for general use by filesystems.
*
* Note: caller is responsible for locking the vfs list, if needed,
* to protect smo and dmo.
*/
void
{
}
static char **
{
int c1 = 0;
int c2 = 0;
char **result;
/*
* First we count both lists of cancel options.
* If either is NULL or has no elements, we return a copy of
* the other.
*/
/* count cancel options in mop1 */;
}
if (c1 == 0)
/* count cancel options in mop2 */;
}
if (c2 == 0)
return (result);
/*
* When we get here, we've got two sets of cancel options;
* we need to merge the two sets. We know that the result
* array has "c1+c2+1" entries and in the end we might shrink
* it.
* Result now has a copy of the c1 entries from mop1; we'll
* now lookup all the entries of mop2 in mop1 and copy it if
* it is unique.
* This operation is O(n^2) but it's only called once per
* filesystem per duplicate option. This is a situation
* which doesn't arise with the filesystems in ON and
* n is generally 1.
*/
break;
}
/*
* Option *sp2 not found in mop1, so copy it.
* The calls to vfs_copycancelopt_extend()
* guarantee that there's enough room.
*/
}
}
}
return (result);
}
/*
* Merge two mount option tables (outer and inner) into one. This is very
* similar to "merging" global variables and automatic variables in C.
*
* This isn't (and doesn't have to be) fast.
*
* This function is *not* for general use by filesystems.
*
* Note: caller is responsible for locking the vfs list, if needed,
* to protect omo, imo & dmo.
*/
void
{
/*
* First determine how much space we need to allocate.
*/
continue;
count++;
}
continue;
char **newcanp;
} else {
/*
* If it's a new option, just copy it over to the first
* free location.
*/
}
}
}
/*
* Functions to set and clear mount options in a mount options table.
*/
/*
* Clear a mount option, if it exists.
*
* The update_mnttab arg indicates whether mops is part of a vfs that is on
* the vfs list.
*/
static void
{
for (i = 0; i < count; i++) {
continue;
continue;
}
if (update_mnttab)
break;
}
}
void
{
int gotlock = 0;
if (VFS_ON_LIST(vfsp)) {
gotlock = 1;
}
if (gotlock)
}
/*
* Set a mount option on. If it's not found in the table, it's silently
* ignored. If the option has MO_IGNORE set, it is still set unless the
* VFS_NOFORCEOPT bit is set in the flags. Also, VFS_DISPLAY/VFS_NODISPLAY flag
* bits can be used to toggle the MO_NODISPLAY bit for the option.
* If the VFS_CREATEOPT flag bit is set then the first option slot with
* MO_EMPTY set is created as the option passed in.
*
* The update_mnttab arg indicates whether mops is part of a vfs that is on
* the vfs list.
*/
static void
{
char *sp;
if (flags & VFS_CREATEOPT) {
flags &= ~VFS_CREATEOPT;
}
}
for (i = 0; i < count; i++) {
if ((flags & VFS_CREATEOPT) == 0)
continue;
else
continue;
}
break;
} else {
}
if (flags & VFS_DISPLAY)
if (flags & VFS_NODISPLAY)
char **cp;
}
if (update_mnttab)
break;
}
}
void
{
int gotlock = 0;
if (VFS_ON_LIST(vfsp)) {
gotlock = 1;
}
if (gotlock)
}
/*
* Add a "tag" option to a mounted file system's options list.
*
* Note: caller is responsible for locking the vfs list, if needed,
* to protect mops.
*/
static mntopt_t *
{
}
return (mop);
}
/*
* Allow users to set arbitrary "tags" in a vfs's mount options.
* Broader use within the kernel is discouraged.
*/
int
{
int found = 0;
int err = 0;
/*
* Find the desired mounted file system
*/
do {
found = 1;
break;
}
if (!found) {
goto out;
}
if (err != 0)
goto out;
/*
* Add tag if it doesn't already exist
*/
int len;
err = ENAMETOOLONG;
goto out;
}
}
goto out;
}
out:
return (err);
}
/*
* Allow users to remove arbitrary "tags" in a vfs's mount options.
* Broader use within the kernel is discouraged.
*/
int
{
int found = 0;
int err = 0;
/*
* Find the desired mounted file system
*/
do {
found = 1;
break;
}
if (!found) {
goto out;
}
if (err != 0)
goto out;
goto out;
}
goto out;
}
out:
return (err);
}
/*
* Function to parse an option string and fill in a mount options table.
* Unknown options are silently ignored. The input option string is modified
* by replacing separators with nulls. If the create flag is set, options
* not found in the table are just added on the fly. The table must have
* an option slot marked MO_EMPTY to add an option on the fly.
*
* This function is *not* for general use by filesystems.
*
* Note: caller is responsible for locking the vfs list, if needed,
* to protect mops..
*/
void
{
int setflg = VFS_NOFORCEOPT;
return;
while (*s != '\0') {
if (p == NULL) {
p = s + strlen(s);
} else {
cp = p; /* save location of comma */
*p++ = '\0'; /* mark end and point to next option */
}
nextop = p;
if (p == NULL) {
} else {
ep = p; /* save location of equals */
*p++ = '\0'; /* end option and point to value */
valp = p;
}
/*
* set option into options table
*/
if (create)
setflg |= VFS_CREATEOPT;
s = nextop;
}
}
/*
* Function to inquire if an option exists in a mount options table.
* Returns a pointer to the option if it exists, else NULL.
*
* This function is *not* for general use by filesystems.
*
* Note: caller is responsible for locking the vfs list, if needed,
* to protect mops.
*/
struct mntopt *
{
for (i = 0; i < count; i++) {
continue;
return (mop);
}
return (NULL);
}
/*
* Function to inquire if an option is set in a mount options table.
* Returns non-zero if set and fills in the arg pointer with a pointer to
* the argument string or NULL if there is no argument string.
*/
static int
{
for (i = 0; i < count; i++) {
continue;
continue;
return (0);
return (1);
}
return (0);
}
int
{
int ret;
return (ret);
}
/*
* Construct a comma separated string of the options set in the given
* mount table, return the string in the given buffer. Return non-zero if
* the buffer would overflow.
*
* This function is *not* for general use by filesystems.
*
* Note: caller is responsible for locking the vfs list, if needed,
* to protect mp.
*/
int
{
char *cp;
uint_t i;
buf[0] = '\0';
if (buf[0] != '\0')
comma = 1;
goto err;
if (comma)
*cp++ = ',';
/*
* Append option value if there is one
*/
int arglen;
goto err;
*cp++ = '=';
}
}
}
return (0);
err:
return (EOVERFLOW);
}
static void
vfs_freecancelopt(char **moc)
{
int ccnt = 0;
char **cp;
ccnt++;
}
}
}
static void
{
}
/*
* Free a mount options table
*
* This function is *not* for general use by filesystems.
*
* Note: caller is responsible for locking the vfs list, if needed,
* to protect mp.
*/
void
{
for (i = 0; i < count; i++) {
}
if (count) {
}
}
/*
* Free any mnttab information recorded in the vfs struct.
* The vfs must not be on the vfs list.
*/
static void
{
/*
* Free device and mount point information
*/
}
}
/*
* Now free mount options information
*/
}
/*
* Return the last mnttab modification time
*/
void
{
*ts = vfs_mnttab_mtime;
}
/*
* See if mnttab is changed
*/
void
{
int changed;
/*
* Note: don't grab vfs list lock before accessing vfs_mnttab_mtime.
* Can lead to deadlock against vfs_mnttab_modtimeupd(). It is safe
* to not grab the vfs list lock because tv_sec is monotonically
* increasing.
*/
if (!changed) {
*phpp = &vfs_pollhd;
}
}
/*
* Update the mnttab modification time and wake up any waiters for
* mnttab changes
*/
void
{
/*
* Attempt to provide unique mtime (like uniqtime but not).
*/
newhrt++;
}
}
int
{
int error;
extern void teardown_vopstats(vfs_t *);
/*
* Get covered vnode. This will be NULL if the vfs is not linked
* into the file system name space (i.e., domount() with MNT_NOSPICE).
*/
/*
* Purge all dnlc entries for this vfs.
*/
(void) dnlc_purge_vfsp(vfsp, 0);
/* For forcible umount, skip VFS_SYNC() since it may hang */
/*
* Lock the vfs to maintain fs status quo during unmount. This
* has to be done after the sync because ufs_update tries to acquire
* the vfs_reflock.
*/
/*
* vfs_remove() will do a VN_RELE(vfsp->vfs_vnodecovered)
* when it frees vfsp so we do a VN_HOLD() so we can
* continue to use coveredvp afterwards.
*/
} else {
/*
* Release the reference to vfs that is not linked
* into the name space.
*/
}
return (error);
}
/*
* Vfs_unmountall() is called by uadmin() to unmount all
* mounted file systems (except the root file system) during shutdown.
* It follows the existing locking protocol when traversing the vfs list
* to sync and unmount vfses. Even though there should be no
* other thread running while the system is shutting down, it is prudent
* to still follow the locking protocol.
*/
void
vfs_unmountall(void)
{
int error;
/*
* Toss all dnlc entries now so that the per-vfs sync
* and unmount operations don't have to slog through
* a bunch of uninteresting vnodes over and over again.
*/
dnlc_purge();
continue;
if (error)
continue;
/*
* Since we dropped the vfslist lock above we must
* verify that next_vfsp still exists, else start over.
*/
break;
}
}
/*
* Called to add an entry to the end of the vfs mount in progress list
*/
void
{
if (vfs_miplist_end != NULL)
else
vfs_miplist = mipp;
}
/*
* Called to remove an entry from the mount in progress list
* Either because the mount completed or it failed.
*/
void
{
for (mipp = vfs_miplist;
}
return; /* shouldn't happen */
if (mipp == vfs_miplist_end)
else
}
/*
* vfs_add is called by a specific filesystem's mount routine to add
* The vfs should already have been locked by the caller.
*
* coveredvp is NULL if this is the root.
*/
void
{
int newflag;
newflag |= VFS_RDONLY;
else
newflag &= ~VFS_RDONLY;
else
if (mflag & MS_NOMNTTAB)
newflag |= VFS_NOMNTTAB;
else
newflag &= ~VFS_NOMNTTAB;
}
}
/*
* Remove a vfs from the vfs list, null out the pointer from the
* covered vnode to the vfs (v_vfsmountedhere), and null out the pointer
* from the vfs to the covered vnode (vfs_vnodecovered). Release the
* reference to the vfs and to the covered vnode.
*
* Called from dounmount after it's confirmed with the file system
* that the unmount is legal.
*/
void
{
/*
* Can't unmount root. Should never happen because fs will
* be busy.
*/
panic("vfs_remove: unmounting root");
/*
* Unhook from the file system name space.
*/
/*
* Release lock and wakeup anybody waiting.
*/
}
/*
* Lock a filesystem to prevent access to it while mounting,
* unmounting and syncing. Return EBUSY immediately if lock
* can't be acquired.
*/
int
{
return (0);
return (EBUSY);
}
int
{
return (0);
return (EBUSY);
}
void
{
}
void
{
}
/*
* Unlock a locked filesystem.
*/
void
{
/*
* vfs_unlock will mimic sema_v behaviour to fix 4748018.
* And these changes should remain for the patch changes as it is.
*/
if (panicstr)
return;
/*
* ve_refcount needs to be dropped twice here.
* 1. To release refernce after a call to vfs_locks_getlock()
* 2. To release the reference from the locking routines like
* vfs_rlock_wait/vfs_wlock_wait/vfs_wlock etc,.
*/
}
/*
* Utility routine that allows a filesystem to construct its
* fsid in "the usual way" - by munging some underlying dev_t and
* the filesystem type number into the 64-bit fsid. Note that
* this implicitly relies on dev_t persistence to make filesystem
* id's persistent.
*
* There's nothing to prevent an individual fs from constructing its
* fsid in a different way, and indeed they should.
*
* Since we want fsids to be 32-bit quantities (so that they can be
* exported identically by either 32-bit or 64-bit APIs, as well as
* the fact that fsid's are "known" to NFS), we compress the device
* number given down to 32-bits, and panic if that isn't possible.
*/
void
{
panic("device number too big for fsid!");
}
int
{
int held;
/*
* vfs_lock_held will mimic sema_held behaviour
* if panicstr is set. And these changes should remain
* for the patch changes as it is.
*/
if (panicstr)
return (1);
return (held);
}
struct _kthread *
{
/*
* vfs_wlock_held will mimic sema_held behaviour
* if panicstr is set. And these changes should remain
* for the patch changes as it is.
*/
if (panicstr)
return (NULL);
return (owner);
}
/*
* vfs list locking.
*
* Rather than manipulate the vfslist lock directly, we abstract into lock
* and unlock routines to allow the locking implementation to be changed for
* clustering.
*
* Whenever the vfs list is modified through its hash links, the overall list
* lock must be obtained before locking the relevant hash bucket. But to see
* whether a given vfs is on the list, it suffices to obtain the lock for the
* hash bucket without getting the overall list lock. (See getvfs() below.)
*/
void
{
}
void
{
}
void
{
}
/*
* Low level worker routines for adding entries to and removing entries from
* the vfs list.
*/
static void
{
int vhno;
/*
* Link into the hash table, inserting it at the end, so that LOFS
* with the same fsid as UFS (or other) file systems will not hide the
* UFS.
*/
if (insert_at_head) {
} else {
continue;
/*
* hp now contains the address of the pointer to update
* to effect the insertion.
*/
}
}
static void
{
int vhno;
/*
* Remove from hash.
*/
goto foundit;
}
goto foundit;
}
}
}
void
{
/*
* The zone that owns the mount is the one that performed the mount.
* Note that this isn't necessarily the same as the zone mounted into.
* The corresponding zone_rele() will be done when the vfs_t is
* being free'd.
*/
/*
* Find the zone mounted into, and put this mount on its vfs list.
*/
/*
* Special casing for the root vfs. This structure is allocated
* statically and hooked onto rootvfs at link time. During the
* vfs_mountroot call at system startup time, the root file system's
* VFS_MOUNTROOT routine will call vfs_add with this root vfs struct
* as argument. The code below must detect and handle this special
* case. The only apparent justification for this special casing is
* to ensure that the root file system appears at the head of the
* list.
*
* XXX: I'm assuming that it's ok to do normal list locking when
* adding the entry for the root file system (this used to be
* done with no locks held).
*/
/*
* Link into the vfs list proper.
*/
/*
* Assert: This vfs is already on the list as its first entry.
* Thus, there's nothing to do.
*/
/*
* Add it to the head of the global zone's vfslist.
*/
} else {
/*
* Link to end of list using vfs_prev (as rootvfs is now a
* doubly linked circular list) so list is in mount order for
* mnttab use.
*/
/*
* Do it again for the zone-private list (which may be NULL).
*/
} else {
}
}
/*
* Link into the hash table, inserting it at the end, so that LOFS
* with the same fsid as UFS (or other) file systems will not hide
* the UFS.
*/
vfs_hash_add(vfsp, 0);
/*
* update the mnttab modification time
*/
}
void
{
/*
* Callers are responsible for preventing attempts to unmount the
* root.
*/
/*
* Remove from hash.
*/
/*
* Remove from vfs list.
*/
/*
* Remove from zone-specific vfs list.
*/
}
/*
* update the mnttab modification time
*/
}
struct vfs *
{
return (vfsp);
}
}
return (NULL);
}
/*
* Returns 0 if the first entry in the list that the device matches has the
* given vfs pointer as well. If the device matches but a different vfs
* pointer is encountered in the list before the given vfs pointer then
* a 1 is returned.
*/
int
{
int retval = 0;
retval = 1;
break;
}
}
return (retval);
}
/*
* Search the vfs list for a specified device. Returns 1, if entry is found
* or 0 if no suitable entry is found.
*/
int
{
int found;
found = 0;
do {
found = 1;
break;
}
return (found);
}
/*
* Search the vfs list for a specified device. Returns a pointer to it
* or NULL if no suitable entry is found. The caller of this routine
* is responsible for releasing the returned vfs pointer.
*/
struct vfs *
{
int found;
found = 0;
do {
/*
* The following could be made more efficient by making
* the entire loop use vfs_zone_next if the call is from
* a zone. The only callers, however, ustat(2) and
* umount2(2), don't seem to justify the added
* complexity at present.
*/
found = 1;
break;
}
}
/*
* Search the vfs list for a specified mntpoint. Returns a pointer to it
* or NULL if no suitable entry is found. The caller of this routine
* is responsible for releasing the returned vfs pointer.
*
* Note that if multiple mntpoints match, the last one matching is
* returned in an attempt to return the "top" mount when overlay
* mounts are covering the same mount point. This is accomplished by starting
* at the end of the list and working our way backwards, stopping at the first
* matching mount.
*/
struct vfs *
vfs_mntpoint2vfsp(const char *mp)
{
if (getzoneid() == GLOBAL_ZONEID) {
/*
* The global zone may see filesystems in any zone.
*/
do {
break;
}
const char *mntpt;
do {
break;
}
}
if (retvfsp)
return (retvfsp);
}
/*
* Search the vfs list for a specified vfsops.
* if vfs entry is found then return 1, else 0.
*/
int
{
int found;
found = 0;
do {
found = 1;
break;
}
return (found);
}
/*
* Allocate an entry in vfssw for a file system type
*/
struct vfssw *
allocate_vfssw(char *type)
{
/*
* The vfssw table uses the empty string to identify an
* available entry; we cannot add any type which has
* a leading NUL. The string length is limited to
* the size of the st_fstype array in struct stat.
*/
return (NULL);
}
if (!ALLOCATED_VFSSW(vswp)) {
return (vswp);
}
return (NULL);
}
/*
* Impose additional layer of translation between vfstype names
* and module names in the filesystem.
*/
static char *
vfs_to_modname(char *vfstype)
{
vfstype = "procfs";
vfstype = "fdfs";
vfstype = "nfs";
}
return (vfstype);
}
/*
* Find a vfssw entry given a file system type name.
* Try to autoload the filesystem if it's not found.
* If it's installed, return the vfssw locked to prevent unloading.
*/
struct vfssw *
vfs_getvfssw(char *type)
{
char *modname;
RLOCK_VFSSW();
/*
* If we haven't yet loaded the root file system, then our
* _init won't be called until later. Allocate vfssw entry,
* because mod_installfs won't be called.
*/
WLOCK_VFSSW();
return (NULL);
}
}
RLOCK_VFSSW();
}
if (!VFS_INSTALLED(vswp)) {
} else
return (vswp);
}
/*
* Try to load the filesystem. Before calling modload(), we drop
* our lock on the VFS switch table, and pick it up after the
* module is loaded. However, there is a potential race: the
* module could be unloaded after the call to modload() completes
* but before we pick up the lock and drive on. Therefore,
* we keep reloading the module until we've loaded the module
* _and_ we have the lock on the VFS switch table.
*/
return (NULL);
RLOCK_VFSSW();
break;
}
return (vswp);
}
/*
* Find a vfssw entry given a file system type name.
*/
struct vfssw *
vfs_getvfsswbyname(char *type)
{
ASSERT(VFSSW_LOCKED());
return (NULL);
return (vswp);
}
}
return (NULL);
}
/*
* Find a vfssw entry given a set of vfsops.
*/
struct vfssw *
{
RLOCK_VFSSW();
return (vswp);
}
}
return (NULL);
}
/*
* Reference a vfssw entry.
*/
void
{
}
/*
* Unreference a vfssw entry.
*/
void
{
}
int sync_timeleft; /* portion of sync_timeout remaining */
static int sync_triesleft; /* portion of sync_retries remaining */
static int new_bufcnt, old_bufcnt;
/*
* Sync all of the mounted filesystems, and then wait for the actual i/o to
* complete. We wait by counting the number of dirty pages and buffers,
* pushing them out using bio_busy() and page_busy(), and then counting again.
* This routine is used during both the uadmin A_SHUTDOWN code as well as
* the SYNC phase of the panic code (see comments in panic.c). It should only
* be used after some higher-level mechanism has quiesced the system so that
* new writes are not being initiated while we are waiting for completion.
*
* To ensure finite running time, our algorithm uses two timeout mechanisms:
* sync_timeleft (a timer implemented by the omnipresent deadman() cyclic), and
* sync_triesleft (a progress counter used by the vfs_syncall() loop below).
* Together these ensure that syncing completes if our i/o paths are stuck.
* The counters are declared above so they can be found easily in the debugger.
*
* The sync_timeleft counter is reset by bio_busy() and page_busy() using the
* vfs_syncprogress() subroutine whenever we make progress through the lists of
* pages and buffers. It is decremented and expired by the deadman() cyclic.
* When vfs_syncall() decides it is done, we disable the deadman() counter by
* setting sync_timeleft to zero. This timer guards against vfs_syncall()
* deadlocking or hanging inside of a broken filesystem or driver routine.
*
* The sync_triesleft counter is updated by vfs_syncall() itself. If we make
* sync_retries consecutive calls to bio_busy() and page_busy() without
* decreasing either the number of dirty buffers or dirty pages below the
* lowest count we have seen so far, we give up and return from vfs_syncall().
*
* Each loop iteration ends with a call to delay() one second to allow time for
* i/o completion and to permit the user time to read our progress messages.
*/
void
vfs_syncall(void)
{
return; /* panic during boot - no filesystems yet */
printf("syncing file systems...");
sync();
while (sync_triesleft > 0) {
if (new_bufcnt == 0 && new_pgcnt == 0)
break;
else
if (new_bufcnt)
if (new_pgcnt)
}
if (new_bufcnt != 0 || new_pgcnt != 0)
printf(" done (not all i/o completed)\n");
else
printf(" done\n");
sync_timeleft = 0;
}
/*
* If we are in the middle of the sync phase of panic, reset sync_timeleft to
* sync_timeout to indicate that we are making progress and the deadman()
* omnipresent cyclic should not yet time us out. Note that it is safe to
* store to sync_timeleft here since the deadman() is firing at high-level
* on top of us. If we are racing with the deadman(), either the deadman()
* will decrement the old value and then we will reset it, or we will
* reset it and then the deadman() will immediately decrement it. In either
* case, correct behavior results.
*/
void
vfs_syncprogress(void)
{
if (panicstr)
}
/*
* Map VFS flags to statvfs flags. These shouldn't really be separate
* flags at all.
*/
{
if (vf & VFS_RDONLY)
if (vf & VFS_NOSETUID)
if (vf & VFS_NOTRUNC)
stf |= ST_NOTRUNC;
return (stf);
}
/*
* Entries for (illegal) fstype 0.
*/
/* ARGSUSED */
int
{
return (0);
}
/*
* Entries for (illegal) fstype 0.
*/
int
vfsstray(void)
{
return (0);
}
/*
* Support for dealing with forced UFS unmount and its interaction with
* LOFS. Could be used by any filesystem.
* See bug 1203132.
*/
int
vfs_EIO(void)
{
return (EIO);
}
/*
* We've gotta define the op for sync separately, since the compiler gets
* confused if we mix and match ANSI and normal style prototypes when
* a "short" argument is present and spits out a warning.
*/
/*ARGSUSED*/
int
{
return (EIO);
}
/*
* Called from startup() to initialize all loaded vfs's
*/
void
vfsinit(void)
{
int error;
extern int vopstats_enabled;
extern void vopstats_startup();
static const fs_operation_def_t EIO_vfsops_template[] = {
};
static const fs_operation_def_t stray_vfsops_template[] = {
};
/* Initialize the vnode cache (file systems may use it during init). */
/* Setup event monitor framework */
fem_init();
/* Initialize the dummy stray file system type. */
/* Initialize the dummy EIO file system. */
if (error != 0) {
/* Shouldn't happen, but not bad enough to panic */
}
/*
* Default EIO_vfs.vfs_flag to VFS_UNMOUNTED so a lookup
* on this vfs can immediately notice it's invalid.
*/
/*
* Call the init routines of non-loadable filesystems only.
* Filesystems which are loaded as separate modules will be
* initialized by the module loading code instead.
*/
RLOCK_VFSSW();
}
if (vopstats_enabled) {
/* EIO_vfs can collect stats, but we don't retrieve them */
}
}
/*
* Increments the vfs reference count by one atomically.
*/
void
{
}
/*
* Decrements the vfs reference count by one atomically. When
* vfs reference count becomes zero, it calls the file system
* specific vfs_freevfs() to free up the resources.
*/
void
{
}
}
/*
* Generic operations vector support.
*
* This is used to build operations vectors for both the vfs and vnode.
* It's normally called only when a file system is loaded.
*
* There are many possible algorithms for this, including the following:
*
* (1) scan the list of known operations; for each, see if the file system
* includes an entry for it, and fill it in as appropriate.
*
* (2) set up defaults for all known operations. scan the list of ops
* supplied by the file system; for each which is both supplied and
* known, fill it in.
*
* (3) sort the lists of known ops & supplied ops; scan the list, filling
* in entries as we go.
*
* we choose (1) for simplicity, and because performance isn't critical here.
* note that (2) could be sped up using a precomputed hash table on known ops.
* (3) could be faster than either, but only if the lists were very large or
* supplied in sorted order.
*
*/
int
const fs_operation_trans_def_t *translation,
const fs_operation_def_t *operations)
{
/*
* Count the number of translations and the number of supplied
* operations.
*/
{
const fs_operation_trans_def_t *p;
for (num_trans = 0, p = translation;
num_trans++, p++)
;
}
{
const fs_operation_def_t *p;
for (num_ops = 0, p = operations;
num_ops++, p++)
;
}
/* Walk through each operation known to our caller. There will be */
/* one entry in the supplied "translation table" for each. */
used = 0;
for (i = 0; i < num_trans; i++) {
int j, found;
char *curname;
/* Look for a matching operation in the list supplied by the */
/* file system. */
found = 0;
for (j = 0; j < num_ops; j++) {
used++;
found = 1;
break;
}
}
/*
* If the file system is using a "placeholder" for default
* or error functions, grab the appropriate function out of
* the translation table. If the file system didn't supply
* this operation at all, use the default function.
*/
if (found) {
if (result == fs_default) {
/* Null values are PROHIBITED */
return (EINVAL);
}
} else {
}
/* Now store the function into the operations vector. */
location = (fs_generic_func_p *)
}
return (0);
}
/* Placeholder functions, should never be called. */
int
fs_error(void)
{
return (0);
}
int
fs_default(void)
{
return (0);
}
#ifdef __sparc
/*
* Part of the implementation of booting off a mirrored root
* involves a change of dev_t for the root device. To
* accomplish this, first remove the existing hash table
* entry for the root device, convert to the new dev_t,
* then re-insert in the hash table at the head of the list.
*/
void
{
}
#else /* x86 NEWBOOT */
int
rootconf()
{
int error;
extern void pm_init();
char *fstyp;
if (error = clboot_rootconf())
return (error);
RLOCK_VFSSW();
/* always mount readonly first */
pm_init();
if (netboot)
(void) strplumb();
if (error)
return (error);
}
/*
* XXX this is called by nfs only and should probably be removed
* If booted with ASKNAME, prompt on the console for a filesystem
* name and return it.
*/
void
{
if (boothowto & RB_ASKNAME) {
}
}
/*
* If server_path exists, then we are booting a diskless
* client. Otherwise, we default to ufs. Zfs should perhaps be
* another property.
*/
static char *
getrootfs(void)
{
extern char *strplumb_get_netdev_path(void);
/* check fstype property; it should be nfsdyn for diskless */
== DDI_SUCCESS) {
}
++netboot;
/* check if path to network interface is specified in bootpath */
== DDI_SUCCESS) {
} else {
/* attempt to determine netdev_path via boot_mac address */
if (netdev_path == NULL)
panic("cannot find boot network interface");
}
return ("nfs");
}
#endif