/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Traverses /etc/mnttab in order to find mounted file systems.
*/
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/mnttab.h>
#include <sys/types.h>
#include <sys/statvfs.h>
#include <strings.h>
#include "libfsmgt.h"
/*
* Private variables
*/
/*
* Private method declarations
*/
static fs_mntlist_t *create_mntlist_entry(struct mnttab mnttab_entry);
static fs_mntlist_t *create_extmntlist_entry(struct extmnttab mnttab_entry);
static struct mnttab *create_mnttab_filter(char *resource, char *mountp,
char *fstype, char *mntopts, char *time);
static void find_overlayed_filesystems(fs_mntlist_t *mnt_list,
boolean_t filtered_list, int *errp);
static void free_mnttab_entry(struct mnttab *mnttab_entry);
static char *is_option(char *opt_string, char *opt, int *errp);
boolean_t is_overlayed(fs_mntlist_t *complete_mnt_list,
char *mountp);
/*
* Public methods
*/
void
fs_free_mount_list(fs_mntlist_t *headp) {
fs_mntlist_t *tmp;
while (headp != NULL) {
tmp = headp->next;
free(headp->resource);
free(headp->mountp);
free(headp->fstype);
free(headp->mntopts);
free(headp->time);
headp->next = NULL;
free(headp);
headp = tmp;
}
} /* fs_free_mount_list */
unsigned long long
fs_get_availablesize(char *mntpnt, int *errp) {
struct statvfs64 stvfs;
unsigned long long availablesize;
*errp = 0;
if (mntpnt == NULL) {
/*
* Set errp to invalid parameter - EINVAL
*/
*errp = EINVAL;
return (0);
}
if (statvfs64(mntpnt, &stvfs) != -1) {
availablesize = stvfs.f_bfree;
availablesize = availablesize * stvfs.f_frsize;
} else {
*errp = errno;
return (0);
} /* if (statvfs64(mntpnt, &stvfs) != -1) */
return (availablesize);
} /* fs_get_availablesize */
unsigned long long
fs_get_avail_for_nonsuperuser_size(char *mntpnt, int *errp) {
struct statvfs64 stvfs;
unsigned long long avail_for_nonsu_size;
*errp = 0;
if (mntpnt == NULL) {
/*
* Set errp to invalid parameter - EINVAL
*/
*errp = EINVAL;
return (0);
}
if (statvfs64(mntpnt, &stvfs) != -1) {
avail_for_nonsu_size = stvfs.f_bavail;
avail_for_nonsu_size = avail_for_nonsu_size * stvfs.f_frsize;
} else {
*errp = errno;
return (0);
} /* if (statvfs64(mntpnt, &stvfs) != -1) */
return (avail_for_nonsu_size);
} /* fs_get_avail_for_nonsuperuser_size(char *mntpnt, int *errp) */
unsigned long long
fs_get_blocksize(char *mntpnt, int *errp) {
struct statvfs64 stvfs;
unsigned long long blocksize;
*errp = 0;
if (mntpnt == NULL) {
/*
* Set errp to invalid parameter - EINVAL
*/
*errp = EINVAL;
return (0);
}
if (statvfs64(mntpnt, &stvfs) != -1) {
blocksize = stvfs.f_bsize;
} else {
*errp = errno;
return (0);
} /* if (statvfs64(mntpnt, &stvfs) != -1) */
return (blocksize);
} /* fs_get_blocksize */
fs_mntlist_t *
fs_get_filtered_mount_list(char *resource, char *mountp, char *fstype,
char *mntopts, char *time, boolean_t find_overlays, int *errp) {
fs_mntlist_t *newp;
fs_mntlist_t *headp;
fs_mntlist_t *tailp;
FILE *fp;
*errp = 0;
headp = NULL;
tailp = NULL;
if ((fp = fopen(MNTTAB, "r")) != NULL) {
struct mnttab mnttab_entry;
struct mnttab *search_entry;
search_entry = create_mnttab_filter(resource, mountp, fstype,
mntopts, time);
if (search_entry == NULL) {
/*
* Out of memory
*/
fs_free_mount_list(headp);
(void) fclose(fp);
*errp = ENOMEM;
return (NULL);
}
while (getmntany(fp, &mnttab_entry, search_entry) == 0) {
/* Add to list to be returned */
newp = create_mntlist_entry(mnttab_entry);
if (newp == NULL) {
/*
* Out of memory
*/
fs_free_mount_list(headp);
(void) fclose(fp);
*errp = ENOMEM;
return (NULL);
}
if (headp == NULL) {
headp = newp;
tailp = newp;
} else {
tailp->next = newp;
tailp = newp;
}
}
free_mnttab_entry(search_entry);
(void) fclose(fp);
if (find_overlays == B_TRUE)
find_overlayed_filesystems(headp, B_TRUE, errp);
} else {
*errp = errno;
} /* if ((fp = fopen(MNTTAB, "r")) != NULL) */
return (headp);
} /* fs_get_filtered_mount_list */
unsigned long
fs_get_fragsize(char *mntpnt, int *errp) {
struct statvfs64 stvfs;
unsigned long fragsize;
*errp = 0;
if (mntpnt == NULL) {
/*
* Set errp to invalid parameter - EINVAL
*/
*errp = EINVAL;
return (0);
}
if (statvfs64(mntpnt, &stvfs) != -1) {
fragsize = stvfs.f_frsize;
} else {
*errp = errno;
return (0);
} /* (statvfs64(mntpnt, &stvfs) != -1) */
return (fragsize);
} /* fs_get_fragsize(char *mntpnt, int *errp) */
unsigned long
fs_get_maxfilenamelen(char *mntpnt, int *errp) {
long int returned_val;
unsigned long maxfilenamelen;
*errp = 0;
if (mntpnt == NULL) {
/*
* Set errp to invalid parameter - EINVAL
*/
*errp = EINVAL;
return (0);
}
returned_val = pathconf(mntpnt, _PC_PATH_MAX);
if (returned_val != -1) {
maxfilenamelen = (unsigned long)returned_val;
} else {
*errp = errno;
return (0);
}
return (maxfilenamelen);
} /* fs_get_maxfilenamelen */
fs_mntlist_t *
fs_get_mounts_by_mntopt(char *mntopt, boolean_t find_overlays, int *errp) {
fs_mntlist_t *newp;
fs_mntlist_t *headp;
fs_mntlist_t *tailp;
FILE *fp;
*errp = 0;
headp = NULL;
tailp = NULL;
if (mntopt == NULL)
return (NULL);
if ((fp = fopen(MNTTAB, "r")) != NULL) {
struct mnttab mnttab_entry;
char *opt_found;
while (getmntent(fp, &mnttab_entry) == 0) {
opt_found = hasmntopt(&mnttab_entry, mntopt);
if (opt_found != NULL) {
/*
* Add to list to be returned
*/
newp = create_mntlist_entry(mnttab_entry);
if (newp == NULL) {
/*
* Out of memory
*/
fs_free_mount_list(headp);
(void) fclose(fp);
*errp = ENOMEM;
return (NULL);
}
if (headp == NULL) {
headp = newp;
tailp = newp;
} else {
tailp->next = newp;
tailp = newp;
}
} /* if (char != NULL) */
}
(void) fclose(fp);
if (find_overlays == B_TRUE)
find_overlayed_filesystems(headp, B_TRUE, errp);
} else {
*errp = errno;
} /* if ((fp = fopen(MNTTAB, "r")) != NULL) */
return (headp);
} /* fs_get_mounts_by_mntpnt */
fs_mntlist_t *
fs_get_mount_list(boolean_t find_overlays, int *errp) {
FILE *fp;
fs_mntlist_t *headp;
fs_mntlist_t *tailp;
fs_mntlist_t *newp;
*errp = 0;
headp = NULL;
tailp = NULL;
if ((fp = fopen(MNTTAB, "r")) != NULL) {
struct extmnttab mnttab_entry;
resetmnttab(fp);
/*
* getextmntent() Is used here so that we can use mnt_major
* and mnt_minor to get the fsid. The fsid is used when
* getting mount information from kstat.
*/
while (getextmntent(fp, &mnttab_entry,
sizeof (struct extmnttab)) == 0) {
newp = create_extmntlist_entry(mnttab_entry);
if (newp == NULL) {
/*
* Out of memory
*/
fs_free_mount_list(headp);
(void) fclose(fp);
*errp = ENOMEM;
return (NULL);
}
if (headp == NULL) {
headp = newp;
tailp = newp;
} else {
tailp->next = newp;
tailp = newp;
}
} /* while (getmntent(fp, &mnttab_entry) == 0) */
(void) fclose(fp);
if (find_overlays)
find_overlayed_filesystems(headp, B_FALSE, errp);
} else {
*errp = errno;
} /* if ((fp = fopen(MNTTAB, "r")) != NULL) */
/*
* Caller must free the mount list
*/
return (headp);
} /* fs_get_mount_list */
boolean_t
fs_is_readonly(char *mntpnt, int *errp) {
struct statvfs64 stvfs;
boolean_t readonly;
*errp = 0;
if (mntpnt == NULL) {
/*
* Set errp to invalid parameter - EINVAL
*/
*errp = EINVAL;
return (B_FALSE);
}
if (statvfs64(mntpnt, &stvfs) != -1) {
readonly = stvfs.f_flag & ST_RDONLY;
} else {
*errp = errno;
return (B_FALSE);
}
return (readonly);
} /* fs_is_readonly */
/*
* This method will parse the given comma delimited option list (optlist) for
* the option passed into the function. If the option (opt) to search for
* is one that sets a value such as onerror=, the value to the right of the "="
* character will be returned from the function. This function expects the
* opt parameter to have the "=" character appended when searching for options
* which set a value.
*
* If the option is found in the given optlist, the function will return the
* option as found in the option list.
* If the option is not found in the given optlist, the function will return
* NULL.
* If an error occurs, the function will return NULL and the errp will
* reflect the error that has occurred.
*
* NOTE: The caller must free the space allocated for the return value by using
* free().
*/
char *
fs_parse_optlist_for_option(char *optlist, char *opt, int *errp) {
const char *delimiter = ",";
char *token;
char *return_value;
char *optlist_copy;
*errp = 0;
optlist_copy = strdup(optlist);
if (optlist_copy == NULL) {
*errp = errno;
return (NULL);
}
token = strtok(optlist_copy, delimiter);
/*
* Check to see if we have found the option.
*/
if (token == NULL) {
free(optlist_copy);
return (NULL);
} else if ((return_value = is_option(token, opt, errp)) != NULL) {
free(optlist_copy);
return (return_value);
}
while (token != NULL) {
token = NULL;
token = strtok(NULL, delimiter);
/*
* If token is NULL then then we are at the end of the list
* and we can return NULL because the option was never found in
* the option list.
*/
if (token == NULL) {
free(optlist_copy);
return (NULL);
} else if ((return_value =
is_option(token, opt, errp)) != NULL) {
free(optlist_copy);
return (return_value);
}
}
free(optlist_copy);
return (NULL);
}
unsigned long long
fs_get_totalsize(char *mntpnt, int *errp) {
struct statvfs64 stvfs;
unsigned long long totalsize;
*errp = 0;
if (mntpnt == NULL) {
/*
* Set errp to invalid parameter - EINVAL
*/
*errp = EINVAL;
return (0);
}
if (statvfs64(mntpnt, &stvfs) != -1) {
totalsize = stvfs.f_blocks;
totalsize = totalsize * stvfs.f_frsize;
} else {
*errp = errno;
return (0);
} /* if (statvfs64(mntpnt, &stvfs) != -1) */
return (totalsize);
} /* fs_get_totalsize */
unsigned long long
fs_get_usedsize(char *mntpnt, int *errp) {
struct statvfs64 stvfs;
unsigned long long usedsize;
*errp = 0;
if (mntpnt == NULL) {
/*
* Set errp to invalid parameter - EINVAL
*/
*errp = EINVAL;
return (0);
}
if (statvfs64(mntpnt, &stvfs) != -1) {
usedsize = stvfs.f_blocks - stvfs.f_bfree;
usedsize = usedsize * stvfs.f_frsize;
} else {
*errp = errno;
return (0);
} /* if (statvfs64(mntpnt, &stvfs) != -1) */
return (usedsize);
} /* fs_get_usedsize */
/*
* Private methods
*/
static fs_mntlist_t *
create_mntlist_entry(struct mnttab mnttab_entry) {
fs_mntlist_t *newp;
newp = (fs_mntlist_t *)calloc((size_t)1,
(size_t)sizeof (fs_mntlist_t));
if (newp == NULL) {
/*
* Out of memory
*/
return (NULL);
}
newp->resource = strdup(mnttab_entry.mnt_special);
if (newp->resource == NULL) {
/*
* Out of memory
*/
fs_free_mount_list(newp);
return (NULL);
}
newp->mountp = strdup(mnttab_entry.mnt_mountp);
if (newp->mountp == NULL) {
/*
* Out of memory
*/
fs_free_mount_list(newp);
return (NULL);
}
newp->fstype = strdup(mnttab_entry.mnt_fstype);
if (newp->fstype == NULL) {
/*
* Out of memory
*/
fs_free_mount_list(newp);
return (NULL);
}
newp->mntopts = strdup(mnttab_entry.mnt_mntopts);
if (newp->mntopts == NULL) {
/*
* Out of memory
*/
fs_free_mount_list(newp);
return (NULL);
}
newp->time = strdup(mnttab_entry.mnt_time);
if (newp->time == NULL) {
/*
* Out of memory
*/
fs_free_mount_list(newp);
return (NULL);
}
newp->next = NULL;
return (newp);
} /* create_mntlist_entry */
static fs_mntlist_t *
create_extmntlist_entry(struct extmnttab mnttab_entry) {
fs_mntlist_t *newp;
newp = (fs_mntlist_t *)calloc((size_t)1,
(size_t)sizeof (fs_mntlist_t));
if (newp == NULL) {
/*
* Out of memory
*/
return (NULL);
}
newp->resource = strdup(mnttab_entry.mnt_special);
if (newp->resource == NULL) {
/*
* Out of memory
*/
fs_free_mount_list(newp);
return (NULL);
}
newp->mountp = strdup(mnttab_entry.mnt_mountp);
if (newp->mountp == NULL) {
/*
* Out of memory
*/
fs_free_mount_list(newp);
return (NULL);
}
newp->fstype = strdup(mnttab_entry.mnt_fstype);
if (newp->fstype == NULL) {
/*
* Out of memory
*/
fs_free_mount_list(newp);
return (NULL);
}
newp->mntopts = strdup(mnttab_entry.mnt_mntopts);
if (newp->mntopts == NULL) {
/*
* Out of memory
*/
fs_free_mount_list(newp);
return (NULL);
}
newp->time = strdup(mnttab_entry.mnt_time);
if (newp->time == NULL) {
/*
* Out of memory
*/
fs_free_mount_list(newp);
return (NULL);
}
newp->major = mnttab_entry.mnt_major;
newp->minor = mnttab_entry.mnt_minor;
newp->next = NULL;
return (newp);
} /* create_extmntlist_entry */
static struct mnttab *
create_mnttab_filter(char *resource, char *mountp, char *fstype, char *mntopts,
char *time) {
struct mnttab *search_entry;
search_entry = (struct mnttab *)calloc((size_t)1,
(size_t)sizeof (struct mnttab));
if (search_entry == NULL) {
/*
* Out of memory
*/
return (NULL);
}
if (resource != NULL) {
search_entry->mnt_special = strdup(resource);
if (search_entry->mnt_special == NULL) {
/*
* Out of memory
*/
free_mnttab_entry(search_entry);
return (NULL);
}
}
if (mountp != NULL) {
search_entry->mnt_mountp = strdup(mountp);
if (search_entry->mnt_mountp == NULL) {
/*
* Out of memory
*/
free_mnttab_entry(search_entry);
return (NULL);
}
}
if (fstype != NULL) {
search_entry->mnt_fstype = strdup(fstype);
if (search_entry->mnt_fstype == NULL) {
/*
* Out of memory
*/
free_mnttab_entry(search_entry);
return (NULL);
}
}
if (mntopts != NULL) {
search_entry->mnt_mntopts = strdup(mntopts);
if (search_entry->mnt_mntopts == NULL) {
/*
* Out of memory
*/
free_mnttab_entry(search_entry);
return (NULL);
}
}
if (time != NULL) {
search_entry->mnt_time = strdup(time);
if (search_entry->mnt_time == NULL) {
/*
* Out of memory
*/
free_mnttab_entry(search_entry);
return (NULL);
}
}
return (search_entry);
} /* create_mnttab_filter */
/*
* We will go through the /etc/mnttab entries to determine the
* instances of overlayed file systems. We do this with the following
* assumptions:
*
* 1.) Entries in mnttab are ordered in the way that the most recent
* mounts are placed at the bottom of /etc/mnttab. Contract to be
* filed:
* 2.) Mnttab entries that are returned from all mnttab library
* functions such as getmntent, getextmntent, and getmntany in the order
* as they are found in /etc/mnttab. Goes along with assumption #1.
* 3.) All automounted NFS file systems will have an autofs entry and
* a NFS entry in /etc/mnttab with the same mount point. Autofs
* entries can be ignored.
* 4.) The device id (dev=) uniquely identifies a mounted file system
* on a host.
*
* Algorithm explanation:
* ----------------------
* For each mnt_list entry
* 1.) Compare it to each /etc/mnttab entry starting at the point in mnttab
* where the mnt_list entry mount is and look for matching mount points,
* but ignore all "autofs" entries
* If a two entries are found with the same mount point mark the mnt_list
* entry as being overlayed.
*/
static void
find_overlayed_filesystems(fs_mntlist_t *mnt_list,
boolean_t filtered_list, int *errp) {
boolean_t exit = B_FALSE;
fs_mntlist_t *mnt_list_to_compare;
fs_mntlist_t *tmp;
*errp = 0;
if (filtered_list == B_TRUE) {
/*
* Get the complete mount list
*/
mnt_list_to_compare = fs_get_mount_list(B_FALSE, errp);
if (mnt_list_to_compare == NULL) {
/*
* If complete_mnt_list is NULL there are two
* possibilites:
* 1.) There are simply no entries in /etc/mnttab.
* 2.) An error was encountered. errp will reflect
* the error.
*/
return;
}
} else {
mnt_list_to_compare = mnt_list;
}
tmp = mnt_list_to_compare;
while (mnt_list != NULL) {
if (!(strcmp(mnt_list->fstype, "autofs") == 0)) {
char *dev_id;
dev_id = fs_parse_optlist_for_option(mnt_list->mntopts,
"dev=", errp);
if (dev_id == NULL) {
return;
}
exit = B_FALSE;
while (tmp != NULL && exit == B_FALSE) {
if (!(strcmp(tmp->fstype, "autofs")) == 0) {
char *tmp_dev_id;
tmp_dev_id =
fs_parse_optlist_for_option(
tmp->mntopts, "dev=", errp);
if (tmp_dev_id == NULL) {
return;
}
if (strcmp(tmp_dev_id, dev_id) == 0) {
/*
* Start searching for an
* overlay here.
*/
mnt_list->overlayed =
is_overlayed(tmp,
mnt_list->mountp);
exit = B_TRUE;
}
free(tmp_dev_id);
}
tmp = tmp->next;
} /* while (tmp != NULL && exit == B_FALSE) */
free(dev_id);
} /* if (!(strcmp(mnt_list->fstype, "autofs") == 0)) */
mnt_list = mnt_list->next;
} /* while (mnt_list != NULL) */
if (filtered_list == B_TRUE)
fs_free_mount_list(mnt_list_to_compare);
} /* find_overlayed_filesystems */
static void
free_mnttab_entry(struct mnttab *mnttab_entry) {
free(mnttab_entry->mnt_special);
free(mnttab_entry->mnt_mountp);
free(mnttab_entry->mnt_fstype);
free(mnttab_entry->mnt_mntopts);
free(mnttab_entry->mnt_time);
free(mnttab_entry);
} /* free_mnttab_entry */
char *
is_option(char *opt_string, char *opt, int *errp) {
char *equalsign = "=";
char *found_equalsign;
char *return_val;
*errp = 0;
found_equalsign = strstr(opt, equalsign);
/*
* If found_equalsign is NULL then we did not find an equal sign
* in the option we are to be looking for.
*/
if (found_equalsign == NULL) {
if (strcmp(opt_string, opt) == 0) {
/*
* We have found the option so return with success.
*/
return_val = strdup(opt);
if (return_val == NULL) {
*errp = errno;
return (NULL);
}
} else {
return_val = NULL;
}
} else {
int counter = 0;
char *opt_found;
char *value;
opt_found = strstr(opt_string, opt);
if (opt_found == NULL) {
return_val = NULL;
} else {
size_t opt_string_len;
size_t opt_len;
size_t value_len;
opt_string_len = strlen(opt_string);
opt_len = strlen(opt);
value_len = opt_string_len - opt_len;
value = (char *)calloc((size_t)(value_len+1),
(size_t)sizeof (char));
if (value == NULL) {
/*
* Out of memory
*/
*errp = ENOMEM;
return (NULL);
}
while (counter <= (value_len-1)) {
value[counter] = opt_string[opt_len+counter];
counter = counter + 1;
}
/*
* Add the null terminating character.
*/
value[counter] = '\0';
return_val = value;
}
} /* else */
return (return_val);
} /* is_option */
boolean_t
is_overlayed(fs_mntlist_t *mnt_list, char *mountp) {
boolean_t ret_val = B_FALSE;
/*
* The first entry in the complete_mnt_list is the same mounted
* file system as the one we are trying to determine whether it is
* overlayed or not. There is no need to compare these mounts.
*/
mnt_list = mnt_list->next;
while (mnt_list != NULL && ret_val == B_FALSE) {
if (!(strcmp(mnt_list->fstype, "autofs") == 0)) {
if (strcmp(mnt_list->mountp, mountp) == 0) {
ret_val = B_TRUE;
} else {
ret_val = B_FALSE;
}
}
mnt_list = mnt_list->next;
}
return (ret_val);
} /* is_overlayed */