mntinfo.c revision bd93c05dbd9b8f1e8d2edf48c777bc881f927608
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
* System includes
*/
#include <stdio.h>
#include <limits.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <libgen.h>
#include <string.h>
#include <wait.h>
#include <signal.h>
#include <malloc.h>
#include <fcntl.h>
#include <sys/systeminfo.h>
#include <pkgstrct.h>
#include <pkginfo.h>
#include <locale.h>
#include <libintl.h>
/*
* consolidation pkg command library includes
*/
#include <pkglib.h>
/*
* local pkg command library includes
*/
#include "install.h"
#include "libinst.h"
#include "libadm.h"
#include "messages.h"
extern char **environ;
static int match_mount; /* This holds the mount of interest. */
int fs_tab_used = 0;
int fs_tab_alloc = 0;
static int fs_list = -1;
#define MOUNT_TABLE MNTTAB
/* returned by already_mounted() */
#define MNT_NOT 0
#define MNT_EXACT 1
#define MNT_AVAIL 2
/* used with is_remote_src() */
#define NOT_REMOTE 0
#define REAL_REMOTE 1
#define SELF_SERVE 2
/*
* HOST_NM_LN needs to be accommodating. The recommended value in the sysinfo
* man page of 257 needs to be expanded. See bugid 4076513.
* 1024 chars is defined in the mnttab.h header as the max size of an entry.
*/
#define HOST_NM_LN MNT_LINE_MAX
/*
* Utilities for getting filesystem information from the mount table.
*
* Note: vanilla SVr4 code (pkginstall/dockspace.c) used the output from
* information about mounted filesystems, so we use the C interfaces to
* the mount table, which also happens to be much faster than running
* another process. Since several of the pkg commands need access to the
* the code has been placed here, to be included in the libinst library.
*/
#define ALLOC_CHUNK 30
/*
* fs_tab_ent_comp - compare fstable entries first by length in reverse
* order, then alphabetically.
*/
static int
{
else
}
/*
* This determines if the source of the mount is from another host. If it's
* from this host, then it might be writable. This returns NOT_REMOTE if it's
* pure local, REAL_REMOTE if it's being served from another host and
* SELF_SERVE if it's being served by the current host.
*/
static int
is_remote_src(char *source)
{
static char host_name[HOST_NM_LN];
static int hn_len;
if (hn_len == 0) {
/* Find out what host this is. */
}
if (source[0] == '/')
return (NOT_REMOTE); /* No server name, so it's local. */
return (NOT_REMOTE); /* it's a floppy disk or something */
/* Scan to the end of the hostname (find the ":"). */
while (*src_ptr != ':')
*src_host_ptr++ = *src_ptr++;
*src_host_ptr = '\0';
/* Multiple hosts: failover with multiple servers; this is remote. */
return (REAL_REMOTE);
return (SELF_SERVE); /* Exporting from itself, it's local. */
return (REAL_REMOTE);
}
/*
* This determines if an apparently writeable filesystem is really writeable
* or if it's been shared over the network with root-restrictive options.
*/
static int
really_write(char *mountpt)
{
return (0); /* may as well be read-only */
/* LINTED do not use creat(); use open(path,... */
return (0); /* can't write */
retval = 0; /* may as well be read-only */
retval = 0; /* too many restrictions */
else
retval = 1;
return (retval);
}
/* This returns the hostname portion of a remote path. */
char *
{
if (fs_tab_used == 0) {
return ("unknown source");
}
if (n < fs_tab_used) {
return ("automounter");
else
} else {
*host_end = '\0';
return (hostname);
}
}
return ("unknown source");
}
/*
* This pulls the path out of a hostpath which may be of the form host:path
* where path is an absolute path. NOTE: If path turns out to be relative,
* this returns NULL.
*/
static char *
{
char *host_end;
return (hostpath); /* It's already legit. */
return (NULL);
}
/*
* This scans the filesystems already mounted to see if this remote mount is
* already in place on the server. This scans the fs_tab for a remote_name
* exactly matching the client's. It stores the current entry number
* corresponding to this mount in the static match_mount.
*
* Returns:
* MNT_NOT Couldn't find it.
* MNT_EXACT This has actually been manually mounted for us
* MNT_AVAIL This is mounted for the server, but needs to be
* loopback mounted from the client's perspective.
*/
static int
char *host_path)
{
int i;
match_mount = -1;
if (fs_tab_used == 0) {
return (MNT_NOT);
}
for (i = 0; i < fs_tab_used; i++) {
/*
* Determine if this has been manually mounted exactly as we
* require. Begin by finding a mount on our current
* mountpoint.
*/
/*
* Now see if it is really the same mount. This isn't
* smart enough to find mounts on top of mounts, but
* assuming there is no conspiracy to fool this
* function, it will be good enough.
*/
if (is_local_host &&
match_mount = i;
return (MNT_EXACT);
}
}
/* Determine if this mount is available to the server. */
match_mount = i;
return (MNT_AVAIL);
}
}
return (MNT_NOT);
}
/*
* This function unmounts all of the loopback mounts created for the client.
* If no client stuff is mounted, this is completely benign, it finds that
* nothing is mounted up and returns. It returns "1" for unmounted everything
* OK and "0" for failure.
*/
int
{
int errcode;
int exit_no;
int n;
int retcode = 1;
int status;
if (fs_tab_used == 0) {
return (1);
}
for (n = 0; n < fs_tab_used-1; n++) {
/* If the filesystem is mounted and this utility did it ... */
char *arg[3];
/* create arglist for umount command */
/* flush standard i/o before creating new process */
/*
* create new process to execute command in;
* vfork is being used to avoid duplicating the parents
* memory space - this means that the child process may
* not modify any of the parents memory including the
* standard i/o descriptors - all the child can do is
* adjust interrupts and open files as a prelude to a
* call to exec().
*/
if (pid < 0) {
/* fork failed! */
retcode = 0;
} else if (pid > 0) {
/*
* this is the parent process
*/
status = 0;
if (pid_return != pid) {
retcode = 0;
}
/*
* If the child was stopped or killed by a
* signal or exied with any code but 0, we
* assume the mount has failed.
*/
retcode = 0;
} else {
fs_tab[n]->cl_mounted = 0;
}
} else {
/*
* this is the child process
*/
int i;
/* reset any signals to default */
for (i = 0; i < NSIG; i++) {
}
/*
* umount error message may be confusing to
* the user.
*/
if (i >= 0) {
}
/* close all file descriptors except stdio */
closefrom(3);
}
}
}
return (retcode);
}
/*
* This function creates the necessary loopback mounts to emulate the client
* configuration with respect to the server. If this is being run on a
* standalone or the installation is actually to the local system, this call
* is benign since srvr_map won't be set anywhere. It returns "1" for mounted
* everything OK and "0" for failure.
*/
int
{
int errcode;
int exit_no;
int n;
int retcode = 1;
int status;
if (fs_tab_used == 0) {
return (1);
}
for (n = fs_tab_used-1; n >= 0; n--) {
/*
* If the filesystem is mounted (meaning available) and the
* apparent filesystem can be mapped to a local filesystem
* AND the local filesystem is not the same as the target
* filesystem, mount it.
*/
char *arg[6];
/* create arglist for mount command */
/* flush standard i/o before creating new process */
/*
* create new process to execute command in;
* vfork is being used to avoid duplicating the parents
* memory space - this means that the child process may
* not modify any of the parents memory including the
* standard i/o descriptors - all the child can do is
* adjust interrupts and open files as a prelude to a
* call to exec().
*/
if (pid < 0) {
/* fork failed! */
retcode = 0;
} else if (pid > 0) {
/*
* this is the parent process
*/
if (pid_return != pid) {
retcode = 0;
}
/*
* If the child was stopped or killed by a
* signal or exied with any code but 0, we
* assume the mount has failed.
*/
retcode = 0;
} else {
}
} else {
/*
* this is the child process
*/
int i;
/* reset all signals to default */
for (i = 0; i < NSIG; i++) {
}
/*
* mount error message may be confusing to
* the user.
*/
if (i >= 0) {
dup2(i, STDERR_FILENO);
}
/* close all file descriptors except stdio */
closefrom(3);
/*NOTREACHED*/
}
}
}
return (retcode);
}
/*
* This function maps path, on a loopback filesystem, back to the real server
* filesystem. fsys_value is the fs_tab[] entry to which the loopback'd path is
* mapped. This returns a pointer to a static area. If the result is needed
* for further processing, it should be strdup()'d or something.
*/
char *
{
static char server_construction[PATH_MAX];
if (fs_tab_used == 0) {
} else if (fsys_value < fs_tab_used) {
(void) snprintf(server_construction,
sizeof (server_construction),
} else {
}
return (server_construction);
}
/* This function sets up the standard parts of the fs_tab. */
static struct fstable *
{
/* Create the array if necessary. */
if (fs_list == -1) {
(unsigned)sizeof (struct fstable),
"filesystem mount data");
if (fs_list == -1) {
return (NULL);
}
}
/*
* Allocate an fstable entry for this mnttab entry.
*/
== NULL) {
return (NULL);
}
/*
* Point fs_tab at the head of the array again, since it may have
* moved due to realloc in ar_next_avail(). If ar_next_avail() realizes
* that there is no more room to grow the array, it reallocates the
* array. Because we stored pointer to that array in fs_tab, we need
* to make sure that it is updated as well.
*/
return (NULL);
}
/*
* Get the length of the 'mount point' name.
*/
/*
* Allocate space for the 'mount point' name.
*/
return (NULL);
}
return (NULL);
}
fs_tab_used++;
return (nfte);
}
/* This function frees all memory associated with the filesystem table. */
void
fs_tab_free(void)
{
int n;
if (fs_tab_used == 0) {
return;
}
for (n = 0; n < fs_tab_used; n++) {
}
}
/* This function scans a string of mount options for a specific keyword. */
static int
{
if (!options) {
} else {
}
*optptr++ = '\0';
return (1);
}
/* Now deal with the remainder. */
return (1);
return (0);
}
/*
* This function constructs a new filesystem table (fs_tab[]) entry based on
* into fs_tab[].
*/
static int
{
/*
* Initialize fstable structure and make the standard entries.
*/
return (1);
/*
* See if this is served from another host.
* Testing the type is cheap; finding the hostname is not.
* At this point, we're using the REAL mnttab; since we're not
* allowed to mount ourself with "NFS", "NFS" must be remote.
* The automount will translate "nfs:self" to a lofs mount.
*/
else
/* It's mounted now (by definition), so we don't have to remap it. */
/*
* This checks the mount commands which establish the most
* basic level of access. Later further tests may be
* necessary to fully qualify this. We set this bit
* preliminarily because we have access to the mount data
* now.
*/
/*
* There's no network involved, so this
* assessment is confirmed.
*/
} else
/* read-only is read-only */
/* Is this coming to us from a server? */
return (0);
}
/*
* This function modifies an existing fs_tab[] entry. It was found mounted up
* exactly the way we would have mounted it in mount_client() only at the
* time we didn't know it was for the client. Now we do, so we're setting the
* various permissions to conform to the client view.
*/
static void
{
/*
* Establish whether the client will see this as served.
*/
}
/*
* This function constructs a new fs_tab[] entry based on
* into fstab[].
*/
static int
{
int use_link;
return (1);
/*
* The file system mounted on the client may or may not be writeable.
* So we hand it over to fsys() to evaluate. This will have the same
*/
use_link = 0;
/*
* Deal here with mount points actually on a system remote
* from the server.
*/
/*
* This filesystem isn't in the current mount table
* meaning it isn't mounted, the current host can't
* write to it and there's no point to mapping it for
* the server.
*/
} else { /* It's MNT_AVAIL. */
/*
* This filesystem is associated with a current
* mountpoint. Since it's mounted, it needs to be
* remapped and it is writable if the real mounted
* filesystem is writeable.
*/
use_link = 1;
}
} else { /* local filesystem */
use_link = 1;
}
/*
* Now we establish whether the client will see this as served.
*/
if (use_link) {
} else {
}
return (0);
}
/*
* get_mntinfo - get the mount table, now dynamically allocated. Returns 0 if
* no problem and 1 if there's a fatal error.
*/
int
{
static char *rn = "/";
char *install_root;
int is_remote;
/*
* Open the mount table for the current host and establish a global
* table that holds data about current mount status.
*/
return (1);
}
/*
* First, review the mounted filesystems on the managing host. This
* may also be the target host but we haven't decided that for sure
* yet.
*/
if (construct_mt(mt))
return (1);
/*
* Now, we see if this installation is to a client. If it is, we scan
* the client's vfstab to determine what filesystems are
* inappropriate to write to. This simply adds the vfstab entries
* representing what will be remote file systems for the client.
* Everything that isn't remote to the client is already accounted
* for in the fs_tab[] so far. If the remote filesystem is really on
* this server, we will write through to the server from this client.
*/
/* OK, this is a legitimate remote client. */
/*
* Since we use the fsys() function later, and it depends on
* an ordered list, we have to sort the list here.
*/
sizeof (struct fstable *), fs_tab_ent_comp);
/*
* Here's where the vfstab for the target is. If we can get
* to it, we'll scan it for what the client will see as
* remote filesystems, otherwise, we'll just skip this.
*/
if (vfstab_file) {
} else {
}
char *link_name;
/*
* Open the vfs table for the target host.
*/
return (1);
}
/* Do this for each entry in the vfstab. */
char client_mountp[PATH_MAX];
int mnt_stat;
/*
* We put it into the fs table if it's
* remote mounted (even from this server) or
* loopback mounted from the client's point
* of view.
*/
if (!(is_remote =
0)
continue; /* not interesting */
/*
* Construct client_mountp by prepending the
* install_root to the 'mount point' name.
*/
(void) strcpy(client_mountp,
} else {
(void) snprintf(client_mountp,
sizeof (client_mountp), "%s%s",
}
/*
* We also skip the entry if the vfs_special
* path and the client_path are the same.
*/
if ((is_remote == SELF_SERVE) &&
client_mountp) == 0)
continue;
/* Determine if this is already mounted. */
} else { /* MNT_NOT */
return (1);
}
}
}
} /* end of if(access()) */
} /* end of if(install_root) */
/* This next one may look stupid, but it can really happen. */
if (fs_tab_used <= 0) {
return (1);
}
/*
* Now that we have the complete list of mounted (or virtually
* mounted) filesystems, we sort the mountpoints in reverse order
* based on the length of the 'mount point' name.
*/
return (1);
} else {
return (0);
}
}
/*
* This function supports dryrun mode by allowing the filesystem table to be
* directly loaded from the continuation file.
*/
int
char *remote_name)
{
return (1);
/* Grab the name and fstype from the new structure. */
/* Copy the basic structure into place. */
/*
* Allocate space for the 'special' name.
*/
return (1);
}
return (0);
}
/*
* Given a path, return the table index of the filesystem the file apparently
* resides on. This doesn't put any time into resolving filesystems that
* refer to other filesystems. It just returns the entry containing this
* path.
*/
{
register int i;
char *path2use;
char *cp;
int pathlen;
/*
* The loop below represents our best effort to identify real path of
* a file, which doesn't need to exist. realpath() returns error for
* nonexistent path, therefore we need to cut off trailing components
* of path until we get path which exists and can be resolved by
* realpath(). Lookup of "/dir/symlink/nonexistent-file" would fail
* to resolve symlink without this.
*/
break;
break;
}
if (found)
else
/* fall back to original path in case of unexpected failure */
/*
* The following algorithm scans the list of attached file systems
* for the one containing path. At this point the file names in
* fs_tab[] are sorted by decreasing length to facilitate the scan.
* The first for() scans past all the file system names too short to
* contain path. The second for() does the actual string comparison.
* It tests first to assure that the comparison is against a complete
* token by assuring that the end of the filesystem name aligns with
* the end of a token in path2use (ie: '/' or NULL) then it does a
* string compare. -- JST
*/
if (fs_tab_used == 0) {
return (-1);
}
for (i = 0; i < fs_tab_used; i++)
continue;
break;
for (; i < fs_tab_used; i++) {
int fs_namelen;
char term_char;
continue;
/*
* "/a", then fs_namelen == 2 and term_char == '/'. If, we're
* term_char (unfortunately) == 'e'. In the case of
* fs_namelen == 1, we check to make sure the filesystem is
* "/" and if it is, we have a guaranteed fit, otherwise we
* do the string compare. -- JST
*/
return (i);
}
}
/*
* It only gets here if the root filesystem is fundamentally corrupt.
* (This can happen!)
*/
return (-1);
}
/*
* This function returns the entry in the fs_tab[] corresponding to the
* actual filesystem of record. It won't return a loopback filesystem entry,
* it will return the filesystem that the loopback filesystem is mounted
* over.
*/
resolved_fsys(char *path)
{
int i = -1;
/* If this isn't a "real" filesystem, resolve the map. */
do {
return (i);
}
/*
* This function returns the srvr_map status based upon the fs_tab entry
* number. This tells us if the server path constructed from the package
* install root is really the target filesystem.
*/
int
{
}
/*
* This function returns the mount status based upon the fs_tab entry
* number. This tells us if there is any hope of gaining access
* to this file system.
*/
int
{
}
/*
* is_fs_writeable_n - given an fstab index, return 1
* if it's writeable, 0 if read-only.
*/
int
{
/*
* If the write access permissions haven't been confirmed, do that
* now. Note that the only reason we need to do the special check is
* in the case of an NFS mount (remote) because we can't determine if
* root has access in any other way.
*/
!fs_tab[n]->write_tested) {
}
}
/*
* is_remote_fs_n - given an fstab index, return 1
* if it's a remote filesystem, 0 if local.
*
* Note: Upon entry, a valid fsys() is required.
*/
int
{
}
/* index-driven is_served() */
int
{
}
/*
* This returns the number of blocks available on the indicated filesystem.
*
* Note: Upon entry, a valid fsys() is required.
*/
{
}
/*
* This returns the number of blocks being used on the indicated filesystem.
*
* Note: Upon entry, a valid fsys() is required.
*/
{
}
/*
* This returns the number of inodes available on the indicated filesystem.
*
* Note: Upon entry, a valid fsys() is required.
*/
{
}
/*
* This returns the number of inodes being used on the indicated filesystem.
*
* Note: Upon entry, a valid fsys() is required.
*/
{
}
/*
* Sets the number of blocks being used on the indicated filesystem.
*
* Note: Upon entry, a valid fsys() is required.
*/
void
{
}
/* Get the filesystem block size. */
{
}
/* Get the filesystem fragment size. */
{
}
/*
* This returns the name of the indicated filesystem.
*/
char *
{
if (fs_tab_used == 0) {
return (NULL);
} else if (n >= fs_tab_used) {
return (NULL);
} else {
}
}
/*
* This returns the remote name of the indicated filesystem.
*
* Note: Upon entry, a valid fsys() is required.
*/
char *
{
return (fs_tab[n]->remote_name);
}
/*
* This function returns the srvr_map status based upon the path.
*/
int
{
if (*fsys_value == BADFSYS)
return (use_srvr_map_n(*fsys_value));
}
/*
* This function returns the mount status based upon the path.
*/
int
{
if (*fsys_value == BADFSYS)
return (is_mounted_n(*fsys_value));
}
/*
* is_fs_writeable - given a cfent entry, return 1
* if it's writeable, 0 if read-only.
*
* Note: Upon exit, a valid fsys() is guaranteed. This is
* an interface requirement.
*/
int
{
if (*fsys_value == BADFSYS)
return (is_fs_writeable_n(*fsys_value));
}
/*
* is_remote_fs - given a cfent entry, return 1
* if it's a remote filesystem, 0 if local.
*
* Also Note: Upon exit, a valid fsys() is guaranteed. This is
* an interface requirement.
*/
int
{
if (*fsys_value == BADFSYS)
return (is_remote_fs_n(*fsys_value));
}
/*
* This function returns the served status of the filesystem. Served means a
* client is getting this file from a server and it is not writeable by the
* client. It has nothing to do with whether or not this particular operation
* (eg: pkgadd or pkgrm) will be writing to it.
*/
int
{
if (*fsys_value == BADFSYS)
return (is_served_n(*fsys_value));
}
/*
* get_remote_path - given a filesystem table index, return the
* path of the filesystem on the remote system. Otherwise,
* return NULL if it's a local filesystem.
*/
char *
{
char *p;
if (!is_remote_fs_n(n))
return (NULL); /* local */
if (!p)
else
p++; /* remote */
return (p);
}
/*
* get_mount_point - given a filesystem table index, return the
* path of the mount point. Otherwise,
* return NULL if it's a local filesystem.
*/
char *
{
if (!is_remote_fs_n(n))
return (NULL); /* local */
}
struct fstable *
{
if (fs_tab_used == 0) {
return (NULL);
} else if (n >= fs_tab_used) {
return (NULL);
} else {
return (fs_tab[n]);
}
}