db_nis.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 (c) 1994 by Sun Microsystems, Inc.
* All rights reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include <syslog.h>
#include <errno.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>
#include <rpcsvc/nfs_prot.h>
#include <netdb.h>
#include <setjmp.h>
#include <netconfig.h>
#include <locale.h>
#include <ulimit.h>
#include <sys/systeminfo.h>
#include "vold.h"
#include "db_nis.h"
/* extern routines */
static void nis_db_lookup(vvnode_t *);
static void nis_db_root();
static bool_t nis_db_testkey(char *, char *, char *);
nis_db_lookup, /* dop_lookup */
nis_db_root, /* dop_root */
nis_db_update, /* dop_update */
nis_db_add, /* dop_add */
nis_db_remove, /* dop_remove */
nis_db_findlabel, /* dop_findlabel */
nis_db_testkey, /* dop_testkey */
"nis+", /* dop_name */
};
static bool_t nis_db_needread();
static u_int version_get();
/*
* Convert a nis+ object to an obj_t
*/
static void convert_obj(int, obj_t *, int, void *);
#define CONV_PUT 2
/*
* convert the nis+ table format into a
* local storage (vol_t) format
*/
#define CONV_LOCALPUT 3
/*
* convert a local description (e.g. a struct
* timeval *) into a vol_t format. Update
* a flag to show something has changed.
*/
#define CONV_FREE 4
/*
* Free space allocated as a result of
* a "put" operation.
*/
#define CONV_MERGE 5
/*
* Merge two object in some reasonable
* way.
*/
/*
* This is what is returned from a CONV_GET operation.
*/
struct conv_get {
void *data;
};
static char *dtabname; /* name of the data table */
static char *ctabname; /* name of the control table */
static char *nis_directory;
#define DB_W_RIGHTS (NIS_READ_ACC)
/*
* Set up the database. If there aren't data and control tables
* there, build them.
*/
db_init()
{
char groupname[NIS_MAXNAMELEN];
int i;
if (*volume_group == '\0') {
} else {
VOLDIR);
VOLDIR);
}
/*
* Just see if the directory is there
* This allows us to print a meaningful message if the user
* hasn't created all the right stuff.
*/
if (*volume_group == NULLC) {
} else {
}
"Nis object %s was not found, it needs to be createed with nismkdir\n"),
namebuf);
return (FALSE);
} else {
return (FALSE);
}
}
/* look for the control table */
found_ctl = 1;
found_ctl = 0;
} else {
/* nis returned some horrible error */
"nis_db_init lookup control error: nis+ says '%s'"),
return (FALSE);
}
/* look for the data table */
found_dat = 1;
found_dat = 0;
} else {
/* nis returned some horrible error */
"nis_db_init lookup data error: nis+ says '%s'"),
return (FALSE);
}
#ifdef DEBUG
/*
* let them know if we have one but not the other.
*/
warning("nis_db_init: Your %s table was missing, ",
warning("but your %s table was there. Could mean",
warning("big trouble.\n");
}
#endif /* DEBUG */
/* control table */
if (found_ctl == 0) {
for (i = 0; i < ncols_control; i++) {
}
#ifdef DEBUG
#endif
"nis_db_init add ctl table error: nis+ says '%s'"),
return (FALSE);
}
/*
* Now, we need to initialize the control table with some
* good poop.
*/
/* name: really just a place holder */
i = 0;
/* xid */
i++;
/* id */
i++;
/* lock */
i++;
#ifdef DEBUG
#endif
"nis_db_init add ctl table error: nis+ says '%s'"),
return (FALSE);
}
}
/* Create the data table */
if (found_dat == 0) {
for (i = 0; i < ncols_data; i++) {
}
#ifdef DEBUG
#endif
"nis_db_init add data table error: nis+ says '%s'"),
return (FALSE);
}
}
return (TRUE);
}
/*
* Pass in the parent of the vnode that we want to lookup.
* In essence, this can be thought of as "we're going to do
* a lookup in this directory, make sure it's in sync with
* the database."
*/
void
{
nis_object *no;
int i;
/*
* this is okay since this really means we
* are poking around in a volume with partitions.
*/
return;
}
/*
* If the directory already has stuff, and we don't
* need to check with the database, just return.
*/
}
return;
}
/*
* Oh god, what an ugly pile of architecture.
*/
np[0] = '/';
}
return;
/*
* we do the setupdated here so we don't lose all of
* our stuff if nis+ flakes out for a while.
*/
}
return;
}
for (i = 0; i < NIS_RES_NUMOBJ(res); i++) {
continue;
}
if (err != 0) {
}
}
}
}
}
/*
* Find root.
*/
void
{
nis_object *no;
int i;
return;
}
return;
return;
}
for (i = 0; i < NIS_RES_NUMOBJ(res); i++) {
} else {
}
}
}
#define NIS_DB_UPDATE_MAXRECUR 5
/*
* write the object out to the database. if it's been changed in the
* database, to merge the change. The only reason for failure
* is if the object moved or was removed.
*/
{
extern void obj_unlink(obj_t *);
int i;
static u_int nis_db_update_recurse;
/* don't even bother looking for temporary objects */
if (node_tmpobj(obj)) {
return (TRUE);
}
"nis_db_update: %s not found in database -- removing\n"),
return (FALSE);
"nis_db_update error on '%s': nis+ says '%s'\n"),
return (FALSE);
}
/* very very bad */
"nis_db_update: database corrupted, run volck\n"));
"nis_db_update: more than one object with id %d\n"),
/*NOTREACHED*/
}
/* conflict! gag. */
}
/* if we're changing the name or directory... */
/* make sure it doesn't already exist */
return (FALSE);
}
}
}
/*
* Take the items noted as needing updating on the object,
* and convert them to numbers for our table.
*/
for (i = 0; i < ncols_data; i++) {
if (nis_upmask & (1<<i)) {
/*
* Note that we only have to convert objects
* that have actually changed.
*/
/* XXX do we have to free the ec_value_val? */
}
}
/*
* I assume this means that the object was
* modified between the read and the write.
* I'll just recurse here. I put a limit
* on the number of recursions. If the max
* number is reached, we just take the object
* from the database and go from there.
*/
} else {
(void) nis_db_update(obj);
}
"nis_db_update error on '%s': nis+ says '%s'\n"),
return (FALSE);
}
#ifdef WHY_IS_THIS_NOT_USED
#endif
return (TRUE);
}
/*
* need to be able to add:
* volumes
* directories
* symlinks
* links
*/
{
int i;
/* assign id */
return (FALSE);
for (i = 0; i < ncols_data; i++) {
}
return (FALSE);
}
return (TRUE);
}
{
"nis_db_remove error on '%s': nis+ says '%s'\n"),
return (FALSE);
}
return (TRUE);
}
static vol_t *
{
int i;
nis_object *no;
vol_t *v;
char *key;
void *d;
return (NULL);
}
for (i = 0; i < NIS_RES_NUMOBJ(res); i++) {
return (v);
}
}
return (NULL);
}
static bool_t
{
return (FALSE);
}
return (TRUE);
}
/*
* Convert a generic object update mask to a nis object update mask.
*/
static u_longlong_t
{
u_longlong_t res = 0;
if (objmask & OBJ_UP_NAME)
if (objmask & OBJ_UP_DIR)
if (objmask & OBJ_UP_UID)
if (objmask & OBJ_UP_GID)
if (objmask & OBJ_UP_MODE)
if (objmask & OBJ_UP_ATIME)
if (objmask & OBJ_UP_MTIME)
if (objmask & OBJ_UP_CTIME)
if (objmask & OBJ_UP_NLINKS)
if (objmask & OBJ_UP_FLAGS)
if (objmask & OBJ_UP_LABEL)
if (objmask & OBJ_UP_LOC)
return (res);
}
/*
* Given a nis+ "item" number, clear the o_upmask entry that maps to it.
*/
static void
{
switch (item) {
case DTAB_NAME:
break;
case DTAB_DIR:
break;
case DTAB_UID:
break;
case DTAB_GID:
break;
case DTAB_MODE:
break;
case DTAB_ATIME:
break;
case DTAB_MTIME:
break;
case DTAB_CTIME:
break;
case DTAB_NLINKS:
break;
case DTAB_PROPS:
break;
case VOL_LABEL:
break;
case VOL_LOCATION:
break;
default:
break;
}
}
#define NIS_DB_READTIME 10
/*
* Check to see if we need to look at the database.
*
* - only read from the database once per rpc or once per
* second, whichever is less frequent. Recall that we only
* update current_time on every rpc call.
* - read the global xid to see if anything has changed.
*/
static bool_t
{
return (FALSE);
}
return (TRUE);
}
void
{
int i;
/*
* This cleans up any mappings we might have,
* frees the minor numbers we've allocated, etc.
*/
}
for (i = 0; i < ncols_data; i++) {
}
}
#define NIS_DB_SLPTIME 1
#define MAX_RETRYS 10
static bool_t
{
char numbuf[NIS_MAXNAMELEN];
nis_object *no;
int retry_count = 0;
/*CONSTCOND*/
while (1) {
retry_count++;
"nisobj_assignid error on ctl: nis+ says '%s'\n"),
return (FALSE);
}
/* very very bad */
return (FALSE);
}
/* XXX probably need to free old ENTRY_VAL */
/*
* I assume this means that the object was
* modified between the read and the write.
* We lost the race to the lock. Loop.
*/
continue;
"obj_id error on control: nis+ says '%s'\n"),
if (retry_count > MAX_RETRYS) {
return (FALSE);
}
(void) sleep(NIS_DB_SLPTIME);
continue;
} else {
/* everything is okay, we a good id */
return (TRUE);
}
}
/*NOTREACHED*/
}
/*
* Do your best to merge these two objects.
*/
/*
* Merge strategy:
* If they are the same, don't do anything.
* If our change is more recent (o_mtime) than the db object, overwite.
* If our change is less recent, take the change and clear upmask bit.
*/
void
{
int i;
for (i = 0; i < ncols_data; i++) {
}
/*
* At this point, the "to" object has an upmask that
* reflects only the changes that we want to make.
*/
}
static obj_t *
{
obj_t o;
int i;
vol_t *v;
void *vp;
for (i = 0; i < DTAB_GEN_END+1; i++) {
}
switch (o.o_type) {
case VV_BLK:
case VV_CHR:
vp = (void *)v;
break;
case VV_DIR:
break;
case VV_LINK:
break;
case VV_SYMLINK:
break;
default:
"nisobj_to_obj: unknown object from database (%d)\n"),
o.o_type);
break;
}
}
}
static void
{ /* top */
extern char *network_username(uid_t);
extern char *network_groupname(gid_t);
extern uid_t network_uid(char *);
extern gid_t network_gid(char *);
char buf[MAXNAMELEN];
if (way == CONV_LOCALPUT) {
}
/*
* Right now, we only support one version. In the future,
* we'll check to see if we are being asked to decode an
* unsupported version and fail that. For now, we just
* check the one.
*/
if (version_get() != DTABLE_VERSION) {
"Don't know how to decode a version %d nis+ db object\n"),
version_get());
return;
}
/*
* Convert generic parts of an object.
*/
switch (item) { /* big switch */
case DTAB_NAME:
switch (way) /* name */ {
case CONV_GET: {
break;
}
case CONV_LOCALPUT:
case CONV_PUT: {
}
break;
}
case CONV_FREE:
break;
case CONV_MERGE: {
break;
}
/*CSTYLED*/
break;
}
}
break;
}
default:
break;
}
break;
case DTAB_DIR:
switch (way) /* dir */ {
case CONV_GET: {
char *s;
/* DSK and MT do not include leading '/' */
s[0] = '/';
s[1] = 'r';
} else {
}
break;
}
case CONV_LOCALPUT:
case CONV_PUT: {
}
break;
}
case CONV_FREE:
break;
case CONV_MERGE: {
break;
}
/*CSTYLED*/
break;
}
}
break;
}
default:
break;
}
break;
/*
* The implementation of version is a bit hacky. I
* know that "name" will always be first and "version"
* will always be second. I keep the version being used
* in a static, so that later calls will do the "put"
* correctly.
*/
case DTAB_VERSION:
switch (way) /* vers */ {
case CONV_GET: {
break;
}
case CONV_PUT: {
break;
}
case CONV_FREE:
case CONV_MERGE:
break;
default:
break;
}
break;
case DTAB_XID:
switch (way) /* xid */ {
case CONV_GET: {
break;
}
case CONV_LOCALPUT: {
break;
}
case CONV_PUT: {
break;
}
case CONV_FREE:
break;
case CONV_MERGE: {
break;
}
/*
* Always take the biggest xid.
*/
break;
}
/*
* changing the mask here is a bit bogus since
* we're about to bump the xid and set the mask
* by hand anyway.
*/
break;
}
default:
break;
}
break;
case DTAB_TYPE:
switch (way) /* type */ {
case CONV_GET: {
case VV_DIR:
break;
case VV_BLK:
case VV_CHR:
break;
case VV_LINK:
break;
case VV_SYMLINK:
break;
}
break;
}
case CONV_PUT: {
}
break;
}
case CONV_FREE:
break;
case CONV_MERGE: {
break;
}
break;
}
default:
break;
}
break;
case DTAB_ID:
switch (way) /* id */ {
case CONV_GET: {
break;
}
case CONV_PUT: {
break;
}
case CONV_FREE:
break;
case CONV_MERGE: {
break;
}
break;
}
default:
break;
}
break;
case DTAB_UID:
switch (way) /* uid */ {
case CONV_GET: {
break;
}
case CONV_PUT: {
break;
}
case CONV_LOCALPUT: {
break;
}
case CONV_FREE:
break;
case CONV_MERGE: {
break;
}
/*CSTYLED*/
break;
}
break;
}
default:
break;
}
break;
case DTAB_GID:
switch (way) /* gid */ {
case CONV_GET: {
break;
}
case CONV_PUT: {
break;
}
case CONV_LOCALPUT: {
break;
}
case CONV_FREE:
break;
case CONV_MERGE: {
break;
}
/*CSTYLED*/
break;
}
break;
}
default:
way);
break;
}
break;
case DTAB_MODE:
switch (way) /* mode */ {
case CONV_GET: {
/* we write it in octal to be nice guys */
break;
}
case CONV_LOCALPUT: {
break;
}
case CONV_PUT: {
/* it automatically figures out octal or whatever */
break;
}
case CONV_FREE:
break;
case CONV_MERGE: {
break;
}
/*CSTYLED*/
break;
}
break;
}
default:
break;
}
break;
case DTAB_NLINKS:
switch (way) /* nlinks */ {
case CONV_GET: {
/* we write it in octal to be nice guys */
break;
}
case CONV_LOCALPUT: {
break;
}
case CONV_PUT: {
/* it automatically figures out octal or whatever */
break;
}
case CONV_FREE:
break;
case CONV_MERGE: {
break;
}
/*CSTYLED*/
break;
}
break;
}
default:
break;
}
break;
case DTAB_ATIME:
case DTAB_MTIME:
case DTAB_CTIME: {
switch (item) {
case DTAB_ATIME:
if (way == CONV_MERGE) {
}
break;
case DTAB_MTIME:
if (way == CONV_MERGE) {
}
break;
case DTAB_CTIME:
if (way == CONV_MERGE) {
}
break;
}
switch (way) {
case CONV_GET: {
break;
}
case CONV_PUT: {
break;
}
case CONV_LOCALPUT: {
break;
}
case CONV_FREE:
break;
case CONV_MERGE: {
/*CSTYLED*/
break;
}
/*
* With time, always use the latest time.
*/
/*CSTYLED*/
break;
}
break;
}
default:
break;
}
break;
}
default:
/*
* The item to be converted wasn't object generic,
* so now we'll try an object specific function.
*/
case VV_BLK:
case VV_CHR:
break;
case VV_DIR:
break;
case VV_LINK:
break;
case VV_SYMLINK:
stuff);
break;
case VV_PART:
break;
default:
break;
}
}
}
static bool_t
{
extern size_t label_size(int);
switch (item) {
case DTAB_PROPS:
switch (way) {
case CONV_GET: {
break;
}
case CONV_PUT: {
break;
}
case CONV_FREE:
break;
case CONV_MERGE: {
break;
}
default:
break;
}
break;
case VOL_MEDTYPE:
switch (way) {
case CONV_GET: {
break;
}
case CONV_LOCALPUT:
case CONV_PUT: {
if (v->v_mtype)
break;
}
case CONV_FREE:
break;
case CONV_MERGE: {
break;
}
/*CSTYLED*/
break;
}
if (v->v_mtype)
break;
}
default:
break;
}
break;
case VOL_LABTYPE:
switch (way) {
case CONV_GET: {
break;
}
case CONV_PUT: {
break;
}
case CONV_FREE:
break;
case CONV_MERGE: {
break;
}
/*CSTYLED*/
break;
}
break;
}
default:
break;
}
break;
case VOL_LABEL:
switch (way) {
case CONV_GET: {
break;
}
case CONV_PUT:
break;
case CONV_LOCALPUT: {
}
/*
* Here we just assume that we've been "given"
* the pointer to the label to manage as
* our very own.
*/
/*
* if we write a new label, both the label
* type and the key may have changed.
*/
#ifdef notdef
#endif
break;
}
case CONV_FREE:
break;
case CONV_MERGE: {
if (label_compare(&v->v_label,
break;
}
/*CSTYLED*/
break;
}
}
lsz);
break;
}
default:
break;
}
break;
case VOL_KEY:
switch (way) {
case CONV_GET: {
break;
}
case CONV_PUT:
case CONV_FREE:
case CONV_MERGE:
/*
* nothing to do here, it's generated from the
* label and not saved.
*/
break;
default:
break;
}
break;
case VOL_LOCATION:
switch (way) {
case CONV_GET: {
if (v->v_location) {
} else {
}
break;
}
case CONV_PUT: {
break;
}
case CONV_LOCALPUT: {
break;
}
case CONV_FREE:
if (v->v_location) {
free(v->v_location);
}
break;
case CONV_MERGE:
/* not sure this is right */
break;
default:
break;
}
break;
default:
break;
}
return (TRUE);
}
/*
* directory attributes ("da") not used, bu here for compatability with
* other routines
*/
/*ARGSUSED*/
static bool_t
{
switch (item) {
case DTAB_PROPS:
switch (way) {
case CONV_GET: {
break;
}
case CONV_PUT:
case CONV_FREE:
case CONV_MERGE:
break;
default:
break;
}
break;
default:
break;
}
return (TRUE);
}
static bool_t
{
char buf[MAXNAMELEN];
switch (item) {
case DTAB_PROPS:
switch (way) {
case CONV_GET: {
break;
}
case CONV_PUT:
case CONV_FREE:
case CONV_MERGE:
break;
default:
break;
}
break;
case LNK_PTR:
switch (way) /* la_id */ {
case CONV_GET: {
break;
}
case CONV_PUT: {
break;
}
case CONV_FREE:
break;
case CONV_MERGE: {
break;
}
break;
}
default:
break;
}
break;
default:
break;
}
return (TRUE);
}
static bool_t
{
switch (item) {
case DTAB_PROPS:
switch (way) {
case CONV_GET: {
break;
}
case CONV_PUT:
case CONV_FREE:
case CONV_MERGE:
break;
default:
way);
break;
}
break;
case SYM_PTR:
switch (way) {
case CONV_GET: {
break;
}
case CONV_PUT:
case CONV_LOCALPUT: {
}
break;
}
case CONV_FREE:
break;
case CONV_MERGE: {
break;
}
/*CSTYLED*/
break;
}
break;
}
default:
break;
}
break;
default:
break;
}
return (TRUE);
}
/*
* partition attributes ("pa") not used, bu here for compatability with
* other routines
*/
/*ARGSUSED*/
static bool_t
{
switch (item) {
case DTAB_PROPS:
switch (way) {
case CONV_GET: {
break;
}
case CONV_PUT:
case CONV_FREE:
case CONV_MERGE:
break;
default:
way);
break;
}
break;
default:
break;
}
return (TRUE);
}
/*
* set the current version for convert to operate on
*/
static void
{
}
/*
* return the current version that convert is operating on
*/
static u_int
{
return (convert_version);
}
static void
{
if (op == XDR_ENCODE) {
xdr_destroy(&xdrs);
} else if (op == XDR_DECODE) {
xdr_destroy(&xdrs);
}
}