vold_dev.c revision 18c2aff776a775d34a4c9893a4c72e0434d68e36
/*
* 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
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <ctype.h>
#include <dirent.h>
#include <dlfcn.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <synch.h>
#include <thread.h>
#include <unistd.h>
/*
* Local include files
*
* There are some dependencies that cause compiles to fail
* if the order is changed in any way.
*/
#include "vold.h"
#include "dev.h"
#include "label.h"
#include "name_factory.h"
extern int vol_fd;
extern bool_t support_nomedia;
#define DEV_HASH_SIZE 64
static struct q dev_q_hash[DEV_HASH_SIZE];
#define DEV_ALLOC_CHUNK 10
static int ndevs = 0;
static int nallocdevs = 0;
/*
* This is the structure in which we maintain the list of all "paths" we've
* seen for a particular device. We use this to keep track of which
* names we are managing so we can un manage them when someone
* changes the configuration file.
*/
struct devpathl {
struct q q;
char *dpl_path;
char *dpl_symtemplate;
int dpl_symidx;
};
void dev_remove_ctldev(struct devs *);
/*
* Driver calls this to add his dsw into the chain. Called at
* initialization time.
*/
void
{
int i, na;
for (i = 0; i < ndevs; i++) {
return;
}
}
if (ndevs == nallocdevs) {
if (devsw == 0) {
sizeof (struct devsw *));
} else {
na = nallocdevs;
sizeof (struct devsw *));
for (i = 0; i < na; i++) {
}
}
}
}
/*
* Call the driver specific routine to return an fd for a
* device.
*/
int
{
return (NULL);
}
}
/*
*/
char *
{
return (NULL);
}
}
{
/*
* Reset the default device file descriptor
* after a medium inserted in the device has
* been repartitioned.
*/
struct devsw *device_swp;
}
debug(1,
"dev_remount: there's no mapping for device (%d.%d)\n",
}
}
} else {
}
}
return (result);
}
/*
* Return the symbolic name of a device.
*/
char *
{
return (NULL);
}
return (dp->dp_symname);
}
/*
* Called from the config file processing code, when it hits
* a 'use' tag.
*/
{
static bool_t dev_checksymname(char *);
static void dev_usepath(struct devsw *, char *, char *, int);
static int dev_avail_devidx(struct devsw *, char *, char *);
extern uid_t network_uid(char *);
extern gid_t network_gid(char *);
int i; /* devsw index */
int j; /* match index */
int idx;
int match_found = 0;
char symbuf[MAXNAMELEN];
debug(10,
"dev_use: %s %s at %s, %s@, u/g=%s/%s, temp_flag=%s, force_flag=%s\n",
/* scan each device type */
for (i = 0; i < ndevs; i++) {
#ifdef DEBUG_DEV_USE
#endif
/*
* make sure the symname doesn't have
* more than one % in it.
*/
symname);
goto dun;
}
/* get a list of paths that match */
break;
}
if (temp_flag) {
}
/* for each match in the list call its driver */
/*LINTED var fmt*/
match_found++;
}
}
}
if (match_found > 0) {
goto dun; /* found at least one match */
}
}
}
if (force_flag) {
debug(10,
"dev_use: returning TRUE since \"force_flag\" set\n");
} else {
debug(10,
"either couldn't find a driver for %s \"%s\","
" or it's already managed\n",
}
dun:
return (res);
}
/*
* Check to make sure the symbolic name doesn't have more than one %d
* in it.
*/
static bool_t
dev_checksymname(char *s)
{
char *p;
int cnt;
for (cnt = 0, p = s; *p != '\0'; p++) {
if (*p == '%') {
/* deal with escaped % case */
if (*(p+1) == '%') {
p++;
continue;
}
cnt++;
}
}
if (cnt > 1) {
return (FALSE);
}
return (TRUE);
}
/*
* When we start to read the config file, we clear out all the
* seen flags.
*/
void
{
int i;
/* clear all the "seen" bits */
for (i = 0; i < ndevs; i++) {
}
}
}
/*
* After we've read the config file, we check to see if we've
* set all the seen flags. If there any that we haven't set,
* we call dsw_close on them to stop using that device.
*/
void
{
int i;
for (i = 0; i < ndevs; i++) {
continue;
}
}
}
void
{
/* device is gone */
return;
}
break;
}
}
}
void
{
extern void async_taskq_clean(struct devs *);
int i;
/* find corresponding devpathl entry */
for (i = 0; i < ndevs; i++) {
if (dpl->dpl_actvol == v)
break;
}
break;
}
return; /* shouldn't happen */
m = NULL;
m = dp->dp_mediump;
/*
* unlabelled medium will be released in dev_unhangvol.
* So we should not release it.
*/
m = NULL;
}
}
}
/*
* d_close() failed to release the
* device. we won't remove the device.
*/
noise("%s is busy. can not close the device.\n",
dp->dp_symname);
return;
}
/*
* device has gone now.
*/
if (m != NULL)
}
static void
{
extern void async_taskq_clean(struct devs *);
/* if this guy wasn't seen this time close him down */
/* close in progress on this device */
return;
}
}
/*
* try umount the device anyway.
*/
m = NULL;
m = dp->dp_mediump;
} else {
/*
* We are closing device directly. If the
* device is unlabelled, then volume will
* be gone by dev_unhangvol(via d_close).
* Therefore, we don't release medium.
*/
m = NULL;
}
}
}
/*
* if action was invoked, we are done. rest of the thing will
* be done by vol_reaper().
*/
return;
/*
* action is not invoked. close it directly.
*/
/*
* d_close() failed to release the device. won't delete
* the device.
*/
noise("%s is busy. can not close the device.\n",
dp->dp_symname);
return;
}
/*
* volume and medium is no longer necessary.
*/
if (m != NULL)
}
}
/*
* Maintain a list of "paths" which a particular driver is using.
* This allows us to just reprocess the config file then see what
* isn't being used anymore.
*/
static void
{
break;
}
}
}
}
/*
* look for the dev index we can use for the particular dev. If the
* device has been managed, and the index has been assigned for the
* particular device path, return the old index immediately.
* Otherwise, first, we create bitmap indicates that which index has
* been used. Once we created bitmap, we pick up the least unused
* number from bitmap, and return it as the device number.
* (eg N in cdromN).
*/
static int
{
unsigned char *bitmap;
/*
* make sure we have same sym name.
*/
continue;
/*
* We have symname and device pathname match.
* return old index for the device.
*/
break;
}
nbits += 32;
}
}
}
if (!found) {
/*
* find least unused number.
*/
/* found free slot */
break;
}
}
}
if (found) {
return (idx);
} else {
}
}
/*
* This interface is currently not well used.
*/
void
{
}
}
/*
* Given a dev, return the dp assocaited with it.
*/
struct devs *
{
return (dp);
}
}
return (NULL);
}
/*
* search all the devs list.
*/
struct devs *
{
int i, j;
for (i = 0; i < DEV_HASH_SIZE; i++) {
return (dp);
}
return (dp);
}
}
}
return (NULL);
}
/*
* search device list and return the number of asynch task which
* is running.
*/
int
dev_nastask(void)
{
int i, nact;
nact = 0;
for (i = 0; i < DEV_HASH_SIZE; i++) {
}
}
return (nact);
}
/*
* Given a dp, return TRUE if there is a piece of media in that
* device, FALSE otherwise.
*/
{
return (TRUE);
}
return (FALSE);
}
/*
* Given a dev, return the associated dsw.
*/
static struct devsw *
{
return (NULL);
}
}
/*
* Check to see if a volume is read-only.
*/
{
return (FALSE);
}
return (dp->dp_writeprot);
}
return (FALSE);
}
/*
* Create a new dp, which represents path.
*/
struct devs *
{
return (NULL);
}
/* add it to the hash table */
return (dp);
}
/*
* Free a dp. Remove it from the hash queue and free storage associated
* with it. Assumes that the caller has already done the unhang.
*/
void
{
/* remove "nomedia" device node */
}
}
/*
* Do all the work associated with ejecting a volume, telling the
* eject program the status, etc, etc. This is called after all
* the actions have run and returned a verdict. The last check that
* can change the results of our ejection is whether the media is
* mounted with an unsafe filesystem. This is done with unsafe_check().
*/
void
{
int err;
struct vioc_eject viej;
int fd;
#ifdef DEBUG
#endif
return;
}
force = v->v_ej_force;
/*
* check to see if we have an "unsafe" file system mounted.
* Returns TRUE if volume is "unsafe" to eject.
*/
if (unsafe_check(v) != FALSE) {
}
}
} else {
}
}
}
/*
* Change our in progress flag...
*/
v->v_ej_inprog = FALSE;
return;
}
}
/*
* Remove the mapping for the device.
*/
(void) dev_unmap(v);
/*
* Update the database entry.
*/
/*
* If this is a polling device, start up the polling
* again.
*/
}
/*
* if volume was unlabelled, it has gone by dev_unhangvol
*/
}
/* signal anything waiting */
#ifdef DEBUG
#endif
/*
* For diskette, create the "nomedia" node after eject(1).
*/
return;
}
/* Create the "nomedia" node for empty removable media device. */
if (support_nomedia) {
}
}
}
}
/*
* This is for the error cases... Something bad has happened, so
* we just spit the thing back out at the user.
*/
void
{
}
/*
* Clean up the devmap associated with this volume.
*/
dev_devmapfree(vol_t *v)
{
uint_t i;
return (TRUE);
}
return (FALSE);
}
for (i = 0; i < v->v_ndev; i++) {
/*
* If the driver still has a mapping for this minor
* number, we can't reuse it. We just mark the
* minor number as being an orphan.
*/
if (dm_flag & DM_MINOR_VALID) {
if (dm_flag & DM_MISSING) {
voldev =
VOLIOCCANCEL, &voldev);
}
} else {
}
}
}
}
v->v_ndev = 0;
v->v_devmap = 0;
return (TRUE);
}
/*
* Build the devmap for this volume.
*/
void
dev_devmap(vol_t *v)
{
int n, p;
return;
}
/*
* This can be considered an error case. The device hasn't
* told us about any partitions, so we'll just use the default
* partition
*/
if (fpart == 0L) {
#ifdef DEBUG
debug(5,
"dev_devmap: dm_voldev[0] set to (%d,%d)\n",
#endif
}
v->v_ndev = 1;
(void) dev_devmapfree(v);
return;
}
return;
}
/*
* Allocate our devmap.
*/
for (n = 0; n < (int)v->v_ndev; n++) {
}
}
/*
* Have the driver tell us what device a partitular
* partition is.
*/
for (p = 0, n = 0; p < V_NUMPAR; p++) {
if (fpart & (1<<p)) {
(void) dev_devmapfree(v);
return;
}
/*
* We just store dm_realdev here to cache it
* so we don't spend our life doing stats of
* the path.
*/
n++;
}
}
}
/*
* Load the devmap for a volume down into the vol driver. If the
* location of the volume hasn't been "confirmed", we bag out...
* unless ndelay is set. The ndelay business is to support non-blocking
* opens. This was required so that you could eject a volume without
* having to read or write it first.
*/
{
/* no location yet, and ??? (XXX: what is this) */
return (FALSE);
}
/*
* always do this to ensure that dm_path is correct
*/
dev_devmap(v);
return (dev_map_dropin(v));
}
dev_map_dropin(vol_t *v)
{
uint_t i; /* dev linked list index */
/* scan all nodes for this volume */
for (i = 0; i < v->v_ndev; i++) {
/* has the location been confirmed ?? */
if (v->v_confirmed) {
/* clear the missing flag */
volume);
} else {
vim.vim_pathlen = 0;
}
/* check for read-only */
}
/* the driver needs to know if this is a floppy device */
}
return (FALSE);
}
}
return (TRUE);
}
/*
* try map the missing volume. If it fails, mark the devmap
* missing, and set V_MISSING so that check_missing will map
* the entry accordingly.
*/
{
int i;
return (TRUE);
return (TRUE);
for (i = 0; i < v->v_ndev; i++) {
break;
}
if (i == v->v_ndev) {
/*
* cannot find minor node in the devmap. what is
* this device??
*/
return (TRUE);
}
return (TRUE);
return (FALSE);
}
/*
* check the devmap which is being taken over, and see if anything
* has been missing. If something exists, create the map. If the
* missing device is being removed, cancel it.
*/
void
{
int i;
struct reap *r;
/*
* If the old volume was unlabelled, the device may have been
* cancelled. we need to uncancel the device by mapping the
* new dev.
*/
return;
return;
(DM_REMOVE|DM_MISSING)) {
/*
* unfortunately the medium inserted in the
* drive is not the same one as before. This
* particular mapping is being removed.
* Therefore, cancelling the volume since
* it's already missing.
*/
continue;
}
/*
* drop the flag in the old devmap so that the
* dev_devmapfree won't do anything further.
*/
}
/*
* If devmap hasn't been created at this point, we delete the
* the reference from the queues.
*/
/*
* we may have reference to the old volume from repq.
* This could happen if volume was removed while
* ejecting(unmounting) medium. Change the reference.
*/
continue;
}
}
/*
* we also may have reference from devpathl if device was
* being closed.
*/
for (i = 0; i < ndevs; i++) {
}
}
}
}
/*
* Copy the devmap as much as possible so that we can
* prevent the growth of minor node.
*/
void
{
int i, j;
/*
* minor hash has reference to the old volume.
* reset it to the new one.
*/
}
/*
* reset devmap pointer, so that it will not be freed.
*/
/*
* we still need to call dev_devmap() so that
* we have right dm_path etc.
*/
}
dev_devmap(nv);
}
return;
/*
* new devmap has been created. do what ever we can do.
*/
if (odmp->dm_realdev == 0)
continue;
if (ndmp->dm_realdev == 0)
continue;
break;
}
/*
* the old device doesn't exist in the new devmap
* flag DM_REMOVE.
*/
continue;
}
/*
* same device has been used in the new mapping.
* first release the new minor, and change the
* volume pointer from old minor to new volume.
*/
/*
* If they've already made mapping, then unmap it.
*/
}
}
/* just make sure */
/*
* clear DM_MAPPED so that subsequent call to
* dev_unmap against old volume won't unmap the device.
*/
}
}
/*
* Remove a mapping for a volume from the driver. Normally called
* on ejection.
*/
static bool_t
{
uint_t i;
if (v->v_flags & V_UNMAPPED)
return (TRUE);
v->v_flags |= V_UNMAPPED;
/* scan all nodes for this volume */
for (i = 0; i < v->v_ndev; i++) {
continue;
/*
* the V_ENXIO flag used to be checked for, here, but
* that didn't allow the setting of "s-enxio" to have
* an immediate effect, so that's now done when the property
* is set
*/
/*
* if it's an unlabeled device, we must cancel
* any pending i/o, because we'll never be able
* to give it back to them.
*/
debug(7,
"dev_unmap: telling driver to UNMAP minor# %d\n",
}
}
/*
* Do the actual unmapping.
*/
/*
* set the flag to say "don't reuse this minor
* number". the purpose of this is to assign
* the minor number to some other piece of media
* while the driver is still mapping it (to
* return errors, for example.
*
* the minor_* code will garbage collect the
* minor numbers for us.
*/
v->v_flags &= ~V_UNMAPPED;
} else {
}
}
v->v_confirmed = FALSE;
return (TRUE);
}
/*
* to the lowest node. This is used by the drivers who want to build
*
* for each component in the path
* - check to see if we already have it
* - stat it in /dev
* - get the modes, et. al.
* - add it in.
*/
vvnode_t *
dev_dirpath(char *path)
{
char **ps;
int found = 0;
int comp;
char namebuf[MAXPATHLEN];
char devnamebuf[MAXPATHLEN];
#ifdef DEBUG
#endif
found++;
break;
}
}
comp++;
if (found == 0) {
/* this should mostly be a debug aid */
path);
}
/*
* XXX: here's where we need to create the /dev
* pathname and stat it to get the modes, uid
* and gid.
*/
/*
* yes, this is ugly and irritating,
* but devroot will not get set until the
* node_lookup the first time through.
*/
}
if (err != 0) {
break;
}
}
}
return (vn);
}
/*
* Given a dp, associate a volume with it in the name space. This
* part of the name space, as specified by the driver. (and, it also
*/
void
{
char *path;
flgs = NODE_TMPID;
}
if (err != 0) {
debug(1,
"dev_hangvol: node_mkobj (chr) failed for \"%s\" (err = %d)\n",
}
}
}
if (err != 0) {
debug(1,
"dev_hangvol: node_mkobj (blk) failed \"%s\" (err = %d)\n",
}
}
}
/*
* Remove any names in the name space that are associated with this
* dp.
*/
void
{
vol_t *v;
#ifdef DEBUG
} else {
}
#endif
}
}
}
}
}
/*
* dev_rename: take care of aliases if we have a volume and
* if it's been renamed.
*/
void
dev_rename(vol_t *v)
{
char *path;
return;
}
return;
}
}
}
/*
* Unlabeled media was just inserted in *dp, create a "fake"
* vol_t to represent it.
*/
vol_t *
{
vol_t *v;
switch (rres) {
case L_UNRECOG:
break;
case L_UNFORMATTED:
break;
case L_NOTUNIQUE:
break;
default:
break;
}
/* set up properties */
v->v_flags |= V_RMONEJECT;
}
v->v_flags |= V_MEJECTABLE;
}
/* ensure the "label type" index is "none" */
/* return pointer to vol structure created */
return (v);
}
/*
* Find the driver that controls this device and ask it to check
* and see if something is there. The driver is responsible for
* generating the check event.
* dev_check returns:
* 0 if it didn't find anything
* 1 if it found something and we already knew about it
* 2 if it found something and we generated an insert event
*/
int
{
int i;
/*
* wildcard -- loop through all the
* devices that have a check routine. If anyone
* returns true, we return true. It's too bad we
* have to wander through the hash table to iterate
* through the devs... oh well.
*/
for (i = 0; i < DEV_HASH_SIZE; i++) {
debug(4,
"dev_check: check device %d.%d\n",
nrval);
if (nrval != 0) {
}
}
}
}
}
return (rval);
}
return (0);
}
}
if (rval != 0) {
}
return (rval);
}
/*
* Return true if a device is being managed by volume management.
*/
int
{
return (FALSE);
}
return (FALSE);
}
return (TRUE);
}
/*
* Return the /dev pathname given the symbolic name of a device.
*/
char *
symname_to_dev(char *symname)
{
int i;
/* just in case something doesn't have any symname */
continue; /* try the next one */
}
/* found it */
break; /* get outta here */
}
}
}
return (res);
}
int
int fd)
{
/*
* Set the symbolic name of the device to a unique name.
* If the smedia_get_device_info() utility returns a vendor-specific
* device name beginning with "ZIP", "zip", "JAZ", or "jaz", use
* "zip" or "jaz" as the base symbolic name and append an integer
* to it that distinguishes it from other symbolic names that begin
* with the same string. If smedia_get_device_info() doesn't return
* a vendor-specific device name beginning with "ZIP", "zip", "JAZ",
* or "jaz", use the generic device name as the base symbolic name
* and append an integer to it that distinguishes it from other
* symbolic names that begin with the same string.
*/
char *base_symbolic_namep;
char *charp;
int reset_result;
int smedia_result;
return (-1);
if (base_symbolic_namep != NULL) {
}
while (*charp != '\0') {
charp++;
}
}
}
if (base_symbolic_namep != NULL &&
if (name_factory_result == NAME_FACTORY_SUCCESS) {
reset_result = 0;
} else {
reset_result = -1;
}
} else {
if (base_symbolic_namep != NULL)
reset_result = 0;
}
if (smedia_result == 0)
(void) smedia_release_handle(handle);
return (reset_result);
}
/*
* create "nomedia" device node
*/
void
{
char path[MAXPATHLEN];
char pathtmp[MAXPATHLEN];
char *s, *nm;
vol_t *v;
return;
}
/*
* first try to create the default device node.
*/
/* remove slice info if device is not floppy */
}
return;
}
*s = '\0';
nm = s + 1;
return;
}
/* device owned by root */
v->v_flags |= V_MEJECTABLE;
}
v->v_confirmed = TRUE;
v->v_ndev = 1;
/*
* create device mapping.
*/
dev_devmap(v);
(void) dev_map_dropin(v);
/*
* create symbolic link from aliases.
*/
return;
}
/* cleanup */
}
}
void
{
}
}
}