devalloc.c revision 7e3e5701c73b753fb9dd17a0cbe0568b4cdda39e
/*
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <limits.h>
#include <fcntl.h>
#include <utime.h>
#include <synch.h>
#include <strings.h>
#include <string.h>
#include <libintl.h>
#include <errno.h>
#include <auth_list.h>
#include <syslog.h>
#include <bsm/devalloc.h>
#define DA_DEFS "/etc/security/tsol/devalloc_defaults"
extern int _readbufline(char *, int, char *, int, int *);
extern char *strtok_r(char *, const char *, char **);
extern char *_strtok_escape(char *, char *, char **);
extern int getdaon(void);
extern int da_matchname(devalloc_t *, char *);
extern int dmap_matchname(devmap_t *, char *);
/*
* The following structure is for recording old entries to be retained.
* We read the entries from the database into a linked list in memory,
* then turn around and write them out again.
*/
typedef struct strentry {
} strentry_t;
/*
* da_check_longindevperm -
* reads /etc/logindevperm and checks if specified device is in the file.
* returns 1 if specified device found in /etc/logindevperm, else returns 0
*/
int
da_check_logindevperm(char *devname)
{
int ret = 0;
int fd = -1;
char *field_delims = " \t\n";
/*
* check if /etc/logindevperm exists and get its size
*/
return (0);
return (0);
}
return (0);
}
return (0);
}
/*
* read and parse /etc/logindevperm
*/
lineno++;
continue; /* ignore blank lines */
/* invalid entry */
continue;
/* empty device list */
continue;
if (plen == 0)
else
fbuf[0] = '\0';
return (slen);
}
}
/*
* check if devname exists in /etc/logindevperm
*/
/*
* device and devname may be one of these types -
*/
return (1);
}
/* all wildcard types */
*ptr = '\0';
return (1);
}
}
}
return (ret);
}
/*
* _da_read_file -
* contents changed since the last time we read it.
* returns size of buffer read, or -1 on failure.
*/
int
int flag)
{
int fd = -1;
int fsize = 0;
*ftime = 0;
/* check the size and the time stamp on the file */
return (-1);
return (-1);
}
/*
* file has been modified since we last read it; or this
* is a forced read.
* read file into the buffer with rw lock.
*/
return (-1);
return (-1);
}
}
return (-1);
}
return (-1);
}
/*
* verify that the file did not change just after we read it.
*/
return (-1);
}
return (-1);
}
}
return (fsize);
}
/*
* _update_zonename -
*/
void
{
int i, j;
int has_zonename = 0;
char *zonename;
return;
return;
}
}
has_zonename = 1;
return;
zonename++;
if (has_zonename) {
return;
}
newsize += 1;
if (has_zonename) {
newsize -= 1;
if (newsize == 0) {
/*
* put 'reserved' in the empty slot.
*/
return;
}
} else {
return;
}
}
for (i = 0, j = 0; i < oldsize; i++) {
continue;
j++;
}
}
}
/*
* _dmap2str -
* converts a device_map entry into a printable string
* returns 0 on success, -1 on error.
*/
/*ARGSUSED*/
static int
{
int length;
return (-1);
return (-1);
dmp->dmap_devlist);
return (-1);
return (0);
}
/*
* _dmap2strentry -
* calls dmap2str to break given devmap_t into printable entry.
* returns pointer to decoded entry, NULL on error.
*/
static strentry_t *
{
return (NULL);
KV_TOKEN_DELIMIT"\\\n\t") != 0) {
return (NULL);
}
return (sep);
}
/*
* fix_optstr -
* removes trailing ':' from buf.
*/
void
fix_optstr(char *buf)
{
char *p = NULL;
*p = ';';
}
/*
* _da2str -
* converts a device_allocate entry into a printable string
* returns 0 on success, -1 on error.
*/
static int
const char *osep)
{
int length;
int matching_entry = 0;
char **dnames;
matching_entry = 1;
break;
}
}
}
return (-1);
return (-1);
if (matching_entry)
DA_RESERVED, sep);
} else {
return (-1);
}
if (dap->da_devopts)
return (-1);
DA_RESERVED, sep);
return (-1);
return (-1);
return (-1);
return (0);
}
/*
* _da2strentry -
* calls da2str to break given devalloc_t into printable entry.
* returns pointer to decoded entry, NULL on error.
*/
static strentry_t *
{
return (NULL);
return (NULL);
}
return (sep);
}
/*
* _def2str
* converts da_defs_t into a printable string.
* returns 0 on success, -1 on error.
*/
static int
{
int length;
return (-1);
KV_ASSIGN, KV_DELIMITER) != 0)
return (-1);
}
return (-1);
return (0);
}
/*
* _def2strentry
* calls _def2str to break given da_defs_t into printable entry.
* returns pointer decoded entry, NULL on error.
*/
static strentry_t *
{
return (NULL);
KV_TOKEN_DELIMIT) != 0) {
return (NULL);
}
return (sep);
}
/*
* _build_defattrs
* cycles through all defattr entries, stores them in memory. removes
* entries with the given search_key (device type).
* returns 0 if given entry not found, 1 if given entry removed, 2 on
* error.
*/
static int
{
int rc = 0;
setdadefent();
/*
* During DA_ADD, we keep an existing entry unless
* we have DA_FORCE set to override that entry.
*/
rc = 0;
}
if (rc == 0) {
enddadefent();
return (2);
}
/* retaining defattr entry: tmp_str->se_str */
if (*head_defent == NULL) {
} else {
}
}
}
enddadefent();
return (rc);
}
/*
* _rebuild_lists -
*
* If dargs->optflag & DA_EVENT, does not assume the dargs list is
* complete or completely believable, since devfsadm caches
* ONLY what it has been exposed to via syseventd.
*
* Cycles through all the entries in the /etc files, stores them
* in memory, takes note of device->dname numbers (i.e. rmdisk0,
* rmdisk12)
*
* Cycles through again, adds dargs entry
* with the name tname%d (lowest unused number for the device type)
* to the list of things for the caller to write out to a file,
* IFF it is a new entry.
*
* It is an error for it to already be there.
*
* Add:
* Returns 0 if successful and 2 on error.
* Remove:
* Returns 0 if not found, 1 if found, 2 on error.
*/
static int
{
int rc = 0;
uint64_t tmp_bitmap = 0;
int tmp = 0;
char *realname;
int found = 0;
return (2);
return (2);
/* read both files, maps first so we can compare actual devices */
/* build device_maps */
setdmapent();
== 1) {
enddmapent();
return (2);
}
enddmapent();
return (2);
}
!= NULL) {
found = 1;
continue; /* don't retain */
}
/*
* Need to know which suffixes are in use
*/
if (rc == 0) {
/*
* Same type, different device. Record
* device suffix already in use.
*/
if (suffix > DA_MAX_DEVNO) {
enddmapent();
return (2);
}
} else {
/*
* Match on add is an error
* or mapping attempt returned error
*/
enddmapent();
return (2);
}
} else
/* add other transaction types as needed */
return (2);
} /* if same type */
enddmapent();
return (2);
}
/* retaining devmap entry: tmp_str->se_str */
if (*head_devmapp == NULL) {
} else {
}
}
enddmapent();
/*
* No need to rewrite the files if the item to be removed is not
* in the files -- wait for another call on another darg.
*/
return (0);
/*
* Since we got here from an event, we know the stored
* devname is a useless guess, since the files had not
* been read when the name was chosen, and we don't keep
* them anywhere else that is sufficiently definitive.
*/
break;
/* Future: support more than 64 hotplug devices per type? */
if (tmp > DA_MAX_DEVNO)
return (2);
}
/*
* Now adjust devalloc list to match devmaps
* Note we now have the correct devname for da_match to use.
*/
setdaent();
if (rc == 1) {
/* logging is on if DA_EVENT is set */
"%s and %s out of sync,"
"%s only in %s.",
}
enddaent();
return (2);
/* make list w/o this entry */
continue;
}
}
enddaent();
return (2);
}
/* retaining devalloc entry: tmp_str->se_str */
if (*head_devallocp == NULL) {
} else {
}
}
enddaent();
/* the caller needs to know if a remove needs to rewrite files */
return (1); /* 0 and 2 cases returned earlier */
return (0); /* Successful DA_ADD */
}
/*
* _build_lists -
* Cycles through all the entries, stores them in memory. removes entries
* with the given search_key (device name or type).
* returns 0 if given entry not found, 1 if given entry removed, 2 on
* error.
*/
static int
{
int rc = 0;
goto dmap_only;
/* build device_allocate */
setdaent();
/*
* During DA_ADD, we keep an existing entry unless
* we have DA_FORCE set to override that entry.
*/
rc = 0;
}
if (rc == 0) {
enddaent();
return (2);
}
/* retaining devalloc entry: tmp_str->se_str */
if (*head_devallocp == NULL) {
} else {
}
}
}
enddaent();
return (rc);
/* build device_maps */
rc = 0;
setdmapent();
/*
* During DA_ADD, we keep an existing entry unless
* we have DA_FORCE set to override that entry.
*/
rc = 0;
}
if (rc == 0) {
enddmapent();
return (2);
}
/* retaining devmap entry: tmp_str->se_str */
if (*head_devmapp == NULL) {
} else {
}
}
}
enddmapent();
return (rc);
}
/*
* _write_defattrs
* writes current entries to devalloc_defaults.
*/
static void
{
}
}
/*
* _write_device_allocate -
* writes current entries in the list to device_allocate.
* frees the strings
*/
static void
{
int is_on = -1;
/*
* put it back before anything else.
* we need to check for the string only if the file
* exists.
*/
if (is_on == 0)
else if (is_on == 1)
}
while (tmp_str) {
}
}
/*
* _write_device_maps -
* writes current entries in the list to device_maps.
* and frees the strings
*/
static void
{
while (tmp_str) {
}
}
/*
* _write_new_defattrs
* writes the new entry to devalloc_defaults.
* returns 0 on success, -1 on error.
*/
static int
{
int count;
char *lasts;
return (-1);
return (0);
count = 1;
}
if (count)
count++;
}
} else {
}
return (0);
}
/*
* _write_new_entry -
* writes the new devalloc_t to device_allocate or the new devmap_t to
* device_maps.
* returns 0 on success, -1 on error.
*/
static int
{
int count;
char *lasts;
if (flag & DA_MAPS_ONLY)
goto dmap_only;
return (-1);
} else {
!= NULL) {
NULL) {
count = 1;
}
if (count)
KV_TOKEN_DELIMIT "\\\n\t");
count++;
}
if (count)
KV_DELIMITER "\\\n\t");
} else {
KV_DELIMITER "\\\n\t");
}
}
if (flag & DA_ALLOC_ONLY)
return (0);
return (-1);
return (0);
}
/*
* _da_lock_devdb -
* locks the database files; lock can be either broken explicitly by
* closing the fd of the lock file, or it expires automatically at process
* termination.
* returns fd of the lock file or -1 on error.
*/
int
_da_lock_devdb(char *rootdir)
{
int lockfd = -1;
int ret;
int count = 0;
int retry = 10;
int retry_sleep;
char *lockfile;
char path[MAXPATHLEN];
} else {
path[0] = '\0';
return (-1);
}
/* cannot open lock file */
return (-1);
/* cannot position lock file */
return (-1);
}
errno = 0;
while (retry > 0) {
count++;
if (ret == 0) {
return (lockfd);
}
/* cannot set lock */
return (-1);
}
retry--;
(void) sleep(retry_sleep);
errno = 0;
}
return (-1);
}
/*
* da_open_devdb -
* opens one or both database files - device_allocate, device_maps - in
* the specified mode.
* locks the database files; lock is either broken explicitly by the
* caller by closing the lock file fd, or it expires automatically at
* process termination.
* writes the file pointer of opened file in the input args - dafp, dmfp.
* returns fd of the lock file on success, -2 if database file does not
* exist, -1 on other errors.
*/
int
{
int oflag = 0;
int fda = -1;
int fdm = -1;
int lockfd = -1;
char *fname;
char *fmode;
char path[MAXPATHLEN];
return (-1);
fmode = "r+F";
fmode = "rF";
}
return (-1);
goto dmap_only;
path[0] = '\0';
/*
* open the device allocation file
*/
} else {
if (lockfd != -1)
return (-1);
}
}
if (lockfd != -1)
}
if (lockfd != -1)
return (-1);
}
if ((flag & DA_ALLOC_ONLY))
goto out;
path[0] = '\0';
/*
* open the device map file
*/
} else {
if (lockfd != -1)
return (-1);
}
}
if (lockfd != -1)
}
if (lockfd != -1)
return (-1);
}
out:
return (lockfd);
}
/*
* _record_on_off -
* adds either DA_ON_STR or DA_OFF_STR to device_allocate
* returns 0 on success, -1 on error.
*/
static int
{
int dafd;
int nsize;
int nitems = 1;
int actionlen;
int str_found = 0;
char *actionstr;
else
return (-1);
str_found = 1;
}
}
/*
* the file never had either the on or the off string;
* make room for it.
*/
str_found = 0;
}
return (-1);
nbuf[0] = '\0';
/* now put the first line that we read in fgets */
return (-1);
}
}
/* now get the rest of the old file */
return (-1);
}
}
return (-1);
}
return (0);
}
/*
* da_update_defattrs -
* writes default attributes to devalloc_defaults
* returns 0 on success, -1 on error.
*/
int
{
char *tmpdefpath = TMPATTRS;
return (0);
return (-1);
return (-1);
}
(void) unlink(tmpdefpath);
return (-1);
}
/*
* examine all entries, remove an old one if required, check
* if a new one needs to be added.
*/
if (rc == 1) {
(void) unlink(tmpdefpath);
return (rc);
}
}
}
/*
* write back any existing entries.
*/
/* add new entries */
} else {
}
rc = -1;
(void) unlink(tmpdefpath);
}
return (rc);
}
/*
* da_update_device -
* Writes existing entries and the SINGLE change requested by da_args,
* to device_allocate and device_maps.
* Returns 0 on success, -1 on error.
*/
int
{
int rc;
int lockfd = -1;
return (0);
/*
* device_allocate and device_maps. updates can be
* done in both or either of the files.
*/
return (0);
}
/*
* name, type and list are required fields for adding a new
* device.
*/
return (-1);
}
return (-1);
return (-1);
return (-1);
return (-1);
}
} else {
}
goto dmap_only;
/*
* device_allocation.
*/
else
if (lockfd == -1)
return (-1);
return (-1);
}
return (-1);
}
/*
* We don't need to parse the file if we are here just to record
*/
return (-1);
}
goto out;
}
/*
* If reacting to a hotplug, read the file entries,
* figure out what dname (tname + a new number) goes to the
* head_devmapp with everything good still in it (_rebuild_lists)
*
* Else examine all the entries, remove an old one if it is
* a duplicate with a device being added, returning the
* remaining list (_build_lists.)
*
* We need to do this only if the file exists already.
*
* Once we have built these lists, we need to free the strings
* in the head_* arrays before returning.
*/
/* for device allocation, the /etc files are the "master" */
&head_devmapp);
else
&head_devmapp);
return (-1);
}
} else
rc = 0;
return (0);
}
/*
* TODO: clean up the workings of DA_UPDATE.
* Due to da_match looking at fields that are missing
* in dargs for DA_UPDATE, the da_match call returns no match,
* but due to the way _da2str combines the devalloc_t info with
* the *dargs info, the DA_ADD_ZONE and DA_REMOVE_ZONE work.
*
* This would not scale if any type of update was ever needed
* from the daemon.
*/
/*
*/
goto out;
return (-1);
}
return (-1);
}
/*
* Write back any non-removed pre-existing entries.
*/
if (head_devmapp != NULL)
out:
/*
* Add any new entries here.
*/
/* add any new entries */
if (rc == 0)
} else {
if (tafp)
if (tmfp)
}
rc = 0;
rc = -1;
}
}
rc = -1;
}
}
return (rc);
}
/*
* da_add_list -
* adds new /dev link name to the linked list of devices.
* returns 0 if link added successfully, -1 on error.
*/
int
{
int instance;
int new_entry = 0;
return (-1);
dname[0] = '\0';
tname = DA_CD_NAME;
dtype = DA_CD_TYPE;
} else {
return (-1);
}
/*
* Add the new link name to the list of links
* that the device 'dname' has.
*/
break;
}
/*
* Either this is the first entry ever, or no matching entry
* was found. Create a new one and add to the list.
*/
instance = 0;
else /* no matching entry */
instance++;
NULL)
return (-1);
new_entry = 1;
/*
* Look for default label range, authorizations and cleaning
* program in devalloc_defaults. If label range is not
* specified in devalloc_defaults, assume it to be admin_low
* to admin_high.
*/
setdadefent();
}
enddadefent();
+ 1; /* +1 for terminator */
}
} else {
plen = 0;
}
if (new_entry) {
}
return (-1);
}
if (plen == 0)
else
" %s", link);
/*
* This is the first entry of this device type.
*/
}
return (0);
}
/*
* da_remove_list -
* removes a /dev link name from the linked list of devices.
* returns type of device if link for that device removed
* successfully, else returns -1 on error.
* if all links for a device are removed, stores that device
* name in devname.
*/
int
{
int flag;
int remove_dev = 0;
return (-1);
else
switch (type) {
case DA_AUDIO:
break;
case DA_CD:
break;
case DA_FLOPPY:
break;
case DA_TAPE:
break;
case DA_RMDISK:
break;
default:
return (-1);
}
remove_dev = 1;
goto remove_dev;
}
}
}
return (-1);
}
/* last name in the list */
remove_dev = 1;
break;
}
}
return (-1);
}
continue;
if (plen == 0) {
slen =
} else {
slen =
}
}
break;
}
}
if (remove_dev == 1) {
}
/*
* what we removed above was the first entry
* in the list. make the next entry to be the
* first.
*/
} else {
/*
* the matching entry was the only entry in the list
* for this type.
*/
}
}
return (flag);
}
/*
* da_rm_list_entry -
*
* The adding of devnames to a devlist and the removal of a
* device are not symmetrical -- hot_cleanup gives a /devices
* name which is used to remove the dentry whose links all point to
* that /devices entry.
*
* The link argument is present if available to make debugging
* easier.
*
* da_rm_list_entry removes an entry from the linked list of devices.
*
* Returns 1 if the devname was removed successfully,
* 0 if not found, -1 for error.
*/
/*ARGSUSED*/
int
{
int retval = 0;
switch (type) {
case DA_AUDIO:
break;
case DA_CD:
break;
case DA_FLOPPY:
break;
case DA_TAPE:
break;
case DA_RMDISK:
break;
default:
return (-1);
}
/* Presumably in daemon mode, no need to remove entry, list is empty */
return (0);
continue;
retval = 1;
break;
}
if (retval == 0)
return (0);
else
return (retval);
}
/*
* da_is_on -
* checks if device allocation feature is turned on.
* returns 1 if on, 0 if off, -1 if status string not
* found in device_allocate.
*/
int
da_is_on()
{
return (getdaon());
}
/*
* da_print_device -
* debug routine to print device entries.
*/
void
{
else
return;
}
}