/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these librararies and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ //%% (c) Copyright 1993, 1994 Hewlett-Packard Company //%% (c) Copyright 1993, 1994 International Business Machines Corp. //%% (c) Copyright 1993, 1994 Sun Microsystems, Inc. //%% (c) Copyright 1993, 1994 Novell, Inc. //%% $TOG: dm_server.C /main/4 1999/10/08 12:01:17 mgreess $ /* * Tool Talk Database Server - dm_server.C * * Copyright (c) 1989 Sun Microsystems, Inc. * * * This file contains the db server wrappers for the following NetISAM * functions: * * isaddindex, isbuild, isclose, iscntl, isdelrec, iserase, isopen, isread, * isrewrec, isstart, iswrite. * * Each wrapper is an rpc procedure that calls the actual NetISAM function * that it wraps on behalf of the client. * * The file also contains functions for implementing access control and * cacheing isopens. */ #include "tt_options.h" #include "dm_access_cache.h" #include "tt_db_server_consts.h" #include "tt_db_msg_q_lock.h" #include "tt_isam_file_utils.h" #include "tt_isam_record_utils.h" #include "tt_db_server_db.h" #include "db_server_globals.h" #include "db/tt_db_key.h" #include "db/db_server.h" #include "db/tt_db_key.h" #include "db/tt_db_key_utils.h" #include "util/tt_string.h" #include "util/tt_path.h" #include "util/tt_log.h" #include "util/tt_port.h" #include "util/tt_gettext.h" #include "util/tt_file_system.h" #include "util/tt_file_system_entry.h" #include "dm/dm_enums.h" #include "dm/dm_recfmts.h" #include #include #include #include #include #include #include #include #include #include #include #include #if defined(ultrix) # include # include # include # define KERNEL # include # undef KERNEL # define MNTTAB "/etc/fstab" const char *TYPE_NFS = "nfs"; const char *TYPE_LOCAL = "ufs"; extern "C" { int getmnt(int *,struct fs_data*, int, int, char*); time_t time(); } #else # include #endif extern "C" { int isaddindex(int, struct keydesc*); } extern "C" { int isbuild(const char*, int, struct keydesc*, int); } extern "C" { int isclose(int); } extern "C" { int iscntl(int, int, ...); } extern "C" { int isdelcurr(int); } extern "C" { int isdelrec(int, long); } extern "C" { int iserase(const char*); } extern "C" { int isfsync(int); } extern "C" { int isopen(const char*, int); } extern "C" { int isread(int, char*, int); } extern "C" { int isrename(char*, char*); } extern "C" { int isrewcurr(int, char*); } extern "C" { int isrewrec(int, long, char*); } extern "C" { int isstart(int, struct keydesc*, int, char*, int); } extern "C" { int iswrite(int, char*); } extern "C" { int isunlock(int); } extern int _tt_auth_level; extern int _tt_gidlen; extern gid_t _tt_gid, _tt_gidlist[NGROUPS]; extern uid_t _tt_uid; extern keydesc _tt_oid_keydesc; extern _Tt_oid_access_queue_ptr _tt_oa_cache; extern _Tt_link_access_queue_ptr _tt_la_cache; extern char _tt_log_file[]; extern _Tt_db_info _tt_db_table[_TT_MAX_ISFD]; /* table of NetISAM db opened */ extern int _tt_refclock; extern time_t _tt_mtab_last_mtime; extern int access_checking; extern void _tt_process_transaction(); extern FILE *errstr; extern char *_tt_get_realpath(char *, char *); int find_endstring(const char *string, const char *end_string); bool_t msg_q_lock(int isfd, const char *record, int length, SVCXPRT *transp); _Tt_string map_old_db_to_new_db(const char *old_db); _Tt_string get_new_db(_Tt_string old_db, int index, _Tt_string new_file); // HACK??? Why do some of these have slashes and some not? const char *_TT_DOCOID_PATH = "/docoid_path"; const char *_TT_LINK_ACCESS = "/link_access"; const char *_TT_LINK_PROP = "link_prop"; const char *_TT_LINK_ENDS = "link_ends"; const char *_TT_OID_ACCESS = "/oid_access"; const char *_TT_OID_CONTAINER = "/oid_container"; const char *_TT_OID_PROP = "oid_prop"; const char *_TT_LOG_FILE = "/log_file"; const int _TT_ROOT_UID = 0; static _Tt_isam_results res; static char _tt_record[ISMAXRECLEN]; enum _Tt_dm_access_category { DM_USER, DM_GROUP, DM_OTHER}; #define DM_TEST_CRASH 0 // XXX We should derive a Tt_path_string class from Tt_string that // has path manipulation member functions built in. void set_real_path (char *path, char *real_path) { if (path[0] != '/') { // a relative path, make it absolute char wd[MAXPATHLEN+1]; if (getcwd(wd,sizeof(wd)) != 0) { path = _Tt_string(wd).cat("/").cat(path); } } _Tt_string right, left, dir, base; left = path; char *p = _tt_get_realpath((char *) path, real_path); while ((p == 0) && (left.len() > 0)) { // realpath failed; drop right components until it // works. base = left.rsplit('/', dir); // Takes care of special case of "/file" where // file does not exist. if ((dir.len() <= 0) && (left[0]=='/')) { dir = "/"; } left = dir; if (right.len() > 0) { right = base.cat("/").cat(right); } else { right = base; } p = _tt_get_realpath((char *)left, real_path); } if (p == 0) { // Give up, use original path. strcpy(real_path, (char *)path); } else if (right.len() > 0) { strcat(real_path, "/"); strcat(real_path, (char *)right); } } /* * _tt_get_record - read a record from the database with the given directory * prefix and db_name. The input record 'rec' contains the key for searching. * The result of the read is returned in _tt_record. If succeeds returns 1. * If fails returns 0. */ int _tt_get_record(int prefix_len, const char *prefix, const char *db_name, char *rec) { static const char *here = "_tt_get_record()"; if (!rec) { /* rec must contain key, even when reading by recnum */ return 0; } char *db = (char *)malloc(prefix_len + strlen(db_name) + 2); memcpy(db, prefix, prefix_len+1); strcpy(db + prefix_len +1, db_name); /* * HACK: Somehow, isopening non-existent ISAM data bases seems * to burn isfds. I can't see in the NetISAM code where this happens, * but it sure is happening. Bypass this by statting the .rec * file before trying to do isopen. */ char *dblong = (char *)malloc(strlen(db)+1+4); int isfd; struct stat statbuf; strcpy(dblong,db); strcat(dblong,".rec"); isfd = stat(dblong,&statbuf); if (-1==isfd) { _tt_syslog(errstr, LOG_ERR, "%s: %m", dblong); free(dblong); return 0; } free(dblong); isfd = cached_isopen(db, ISINOUT+ISFIXLEN+ISMANULOCK); free(db); if (isfd == -1) { _tt_syslog(errstr, LOG_ERR, "%s: isopen(): %d", here, iserrno); return 0; } if (isstart(isfd, &_tt_oid_keydesc, OID_KEY_LENGTH, rec, ISEQUAL) == -1) { _tt_syslog(errstr, LOG_ERR, "%s: isstart(): %d", here, iserrno); cached_isclose(isfd); return 0; } memcpy(_tt_record, rec, OID_KEY_LENGTH); if (isread(isfd, _tt_record, ISNEXT) == -1) { _tt_syslog(errstr, LOG_ERR, "%s: isread(): %d", here, iserrno); cached_isclose(isfd); return 0; } if (cached_isclose(isfd) == -1) { _tt_syslog(errstr, LOG_ERR, "%s: isclose(): %d", here, iserrno); return 0; } return 1; } /* * _tt_write_record - write a record to the database with the given directory * prefix and db_name. The input record 'rec' contains record for writing. * If succeeds returns 1. If fails returns 0. */ int _tt_write_record(int prefix_len, const char *prefix, const char *db_name, char *rec, int reclen) { static const char *here = "_tt_write_record()"; if (!rec) { return 0; } char *db = (char *)malloc(prefix_len + strlen(db_name) + 2); memcpy(db, prefix, prefix_len+1); strcpy(db + prefix_len +1, db_name); /* * HACK: Somehow, isopening non-existent ISAM data bases seems * to burn isfds. I can't see in the NetISAM code where this happens, * but it sure is happening. Bypass this by statting the .rec * file before trying to do isopen. */ char *dblong = (char *)malloc(strlen(db)+1+4); int isfd; struct stat statbuf; strcpy(dblong,db); strcat(dblong,".rec"); isfd = stat(dblong,&statbuf); if (-1==isfd) { _tt_syslog(errstr, LOG_ERR, "%s: %m", dblong); free(dblong); return 0; } free(dblong); isfd = cached_isopen(db, ISINOUT+ISFIXLEN+ISMANULOCK); free(db); if (isfd == -1) { _tt_syslog(errstr, LOG_ERR, "%s: isopen(): %d", here, iserrno); return 0; } memcpy(_tt_record, rec, OID_KEY_LENGTH); isreclen = reclen; if (iswrite(isfd, rec) == -1) { _tt_syslog(errstr, LOG_ERR, "%s: iswrite(): %d", here, iserrno); cached_isclose(isfd); return 0; } if (cached_isclose(isfd) == -1) { _tt_syslog(errstr, LOG_ERR, "%s: isclose(): %d", here, iserrno); return 0; } return 1; } /* * _tt_get_oid_access - get the input oid's owner, group, and mode */ int _tt_get_oid_access(char *key, int prefix_len, char *db_path, uid_t &uid, gid_t &group, mode_t &mode) { /* lookup in LRU cache */ _Tt_oid_access_ptr oa = _tt_oa_cache->lookup(key); if (oa.is_null()) { /* if not in cache, get from OID-ACCESS database */ if (_tt_get_record(prefix_len, db_path, _TT_OID_ACCESS, key)) { oa = new _Tt_oid_access(_tt_record); if (!oa.is_null()) { _tt_oa_cache->enqueue(oa); /* put in LRU cache */ } } else { // no oid record in access table return 1; } } else { _tt_oa_cache->promote(oa); /* update the LRU cache */ } uid = oa->user(); group = oa->group(); mode = oa->mode(); return 0; } /* * _tt_get_link_permissions - get the link's permissions, user, and groups. * Returns 0 if the link has no access info, 1 if succeeds. */ int _tt_get_link_permissions(char *key, int prefix_len, char *db_path, uid_t &uid, gid_t &group, mode_t &mode) { /* lookup in LRU cache */ _Tt_link_access_ptr la = _tt_la_cache->lookup(key); if (la.is_null()) { /* if not in cache, get from LINK-ACCESS database */ if (_tt_get_record(prefix_len, db_path, _TT_LINK_ACCESS, key)) { la = new _Tt_link_access(_tt_record); if (!la.is_null()) { _tt_la_cache->enqueue(la); /* put in LRU cache */ } } else { return 1; } } else { _tt_la_cache->promote(la); /* update the LRU cache */ } uid = la->user(); group = la->group(); mode = la->mode(); return 0; } /* * Check for stale NetISAM file descriptor. Returns 1 if client's uid matches * database opener's uid. Otherwise returns 0. */ int _tt_check_stale_isfd(int isfd) { if (_tt_uid != _tt_db_table[isfd].opener_uid) { return 0; } return 1; } /* * _tt_oid_accessp - this function determines whether the client with the * extracted uid and gids have read or write permissions as specified by the * input mode to the OID-PROP or LINK-PROP records. If no access property * is found, an error has occurred and no permission is given. * If the client has the access permission, returns 1. Otherwise, returns 0. */ int _tt_oid_accessp(int _isfd, char *rec, int mode) { static const char *here = "_tt_oid_accessp()"; int prefix_len; mode_t request_mask = 0; _Tt_dm_access_category category = DM_OTHER; uid_t oid_uid, file_uid; gid_t oid_group, file_group; mode_t oid_mode, file_mode; if (! access_checking) { // access checking turned off return 1; } if (_tt_uid == _TT_ROOT_UID) { // The superuser always has access return 1; } if (!_tt_check_stale_isfd(_isfd)) { _tt_syslog(errstr, LOG_ERR, "%s: _tt_check_stale_isfd() == 0", here ); return 0; } char *db_path = _tt_db_table[_isfd].db_path; if (!db_path) { _tt_syslog(errstr, LOG_ERR, "%s: db_path == 0", here); return 0; } prefix_len = _Tt_dirname(db_path); if (prefix_len < 0) { _tt_syslog(errstr, LOG_ERR, "%s: _Tt_dirname(db_path) < 0", here); return 0; } int basename_len = _Tt_basename(db_path); char *db_basename = db_path + basename_len; if (strcmp(db_basename, _TT_OID_PROP) == 0) { /* get the OID-ACCESS info */ if (_tt_get_oid_access(rec, prefix_len, db_path, oid_uid, oid_group, oid_mode)) { // no record, error _tt_syslog(errstr, LOG_ERR, "_tt_get_oid_access() != 0"); return 0; } /* get the document oid */ if (!_tt_get_record(prefix_len, db_path, _TT_OID_CONTAINER, rec)) { // only a doc oid has no mapping, allow access return 1; } /* get the document path */ if (!_tt_get_record(prefix_len, db_path, _TT_DOCOID_PATH, _tt_record + OID_KEY_LENGTH)) { _tt_syslog(errstr, LOG_ERR, "_tt_get_record(,,_TT_DOCOID_PATH,) == 0"); return 0; } char *path = _tt_record + OID_KEY_LENGTH; char *p = strchr(path, ':'); if (!p) { _tt_syslog(errstr, LOG_ERR, "strchr(path, ':') != 0"); return 0; } /* get the document's permissions */ struct stat buf; if (stat(p+1, &buf) != -1) { file_uid = buf.st_uid; file_group = buf.st_gid; file_mode = buf.st_mode; } else { file_uid = oid_uid; file_group = oid_group; file_mode = oid_mode; } if (_tt_uid == file_uid || _tt_uid == oid_uid) { category = DM_USER; } else { for (int i = 0; i < _tt_gidlen; i++) { if (_tt_gidlist[i] == file_group) { category = DM_GROUP; break; } } } if (file_mode == (mode_t)-1) { // no mode, assume OK return 1; } /* check access request against permissions */ switch (category) { case DM_USER: // The owner is allowed all priviledges return 1; /* this left here in case we want to check someday * switch (mode) { * case 'r': * request_mask = S_IRUSR; * break; * case 'w': * request_mask = S_IWUSR; * break; * case ('r' + 'w'): * request_mask = S_IRUSR + S_IWUSR; * break; * } * break; */ case DM_GROUP: switch (mode) { case 'r': request_mask = S_IRGRP; break; case 'w': request_mask = S_IWGRP; break; case ('r' + 'w'): request_mask = S_IRGRP + S_IWGRP; break; } break; case DM_OTHER: switch (mode) { case 'r': request_mask = S_IROTH; break; case 'w': request_mask = S_IWOTH; break; case ('r' + 'w'): request_mask = S_IROTH + S_IWOTH; break; } break; } if ((request_mask & file_mode) == 0 && (oid_mode == (mode_t)-1 || (request_mask & oid_mode) == 0)) { return 0; } } else if (strcmp(db_basename, _TT_LINK_PROP) == 0 || strcmp(db_basename, _TT_LINK_ENDS) == 0) { /* get the LINK access permissions */ if (_tt_get_link_permissions(rec, prefix_len, db_path, oid_uid, oid_group, oid_mode)) { // no link permissions, error _tt_syslog(errstr, LOG_ERR, "_tt_get_link_permissions() != 0"); return 0; } if (_tt_uid == oid_uid) { category = DM_USER; } else { for (int i = 0; i < _tt_gidlen; i++) { if (_tt_gidlist[i] == oid_group) { category = DM_GROUP; break; } } } if (oid_mode == (mode_t)-1) { // no mode, assume OK return 1; } /* check LINK access request against permissions */ switch (category) { case DM_USER: switch (mode) { case 'r': // everything fails if the owner can't read // the link, so just let him. return 1; // request_mask = S_IRUSR; // break; case 'w': request_mask = S_IWUSR; break; case ('r' + 'w'): request_mask = S_IRUSR + S_IWUSR; break; } break; case DM_GROUP: switch (mode) { case 'r': request_mask = S_IRGRP; break; case 'w': request_mask = S_IWGRP; break; case ('r' + 'w'): request_mask = S_IRGRP + S_IWGRP; break; } break; case DM_OTHER: switch (mode) { case 'r': request_mask = S_IROTH; break; case 'w': request_mask = S_IWOTH; break; case ('r' + 'w'): request_mask = S_IROTH + S_IWOTH; break; } break; } if ((request_mask & oid_mode) == 0) { return 0; } } return 1; } /* * Read the oid owner as a uid. */ _Tt_dm_status _tt_read_oid_user(int isfd, const char *key, uid_t &uid, gid_t &group, mode_t &mode) { static const char *here = "_tt_read_oid_user()"; memcpy(_tt_record, key, OID_KEY_LENGTH); if (isstart(isfd, &_tt_oid_keydesc, OID_KEY_LENGTH, _tt_record, ISEQUAL) == -1) { _tt_syslog(errstr, LOG_ERR, "%s: isstart(): %d", here, iserrno); if (iserrno == ENOREC) { return DM_NO_RECORD; } else { return DM_ERROR; } } if (isread(isfd, _tt_record, ISNEXT) == -1) { _tt_syslog(errstr, LOG_ERR, "%s: isread(): %d", here, iserrno); return DM_ERROR; } _Tt_oid_access_ptr oa = new _Tt_oid_access(_tt_record); uid = oa->user(); group = oa->group(); mode = oa->mode(); return DM_OK; } /* * Write out the oid access fields. * * The uid can only be written if there does not already exist one. * The group can only be set by the owner and must be a group of the user. * The mode can only be changed by the owner. * * The superuser can change anything. */ _Tt_dm_status _tt_write_oid_access(int isfd, const char *key, uid_t uid, gid_t group, mode_t mode) { static const char *here = "_tt_write_oid_access()"; _Tt_oid_access_ptr oa; memcpy(_tt_record, key, OID_KEY_LENGTH); if (isstart(isfd, &_tt_oid_keydesc, OID_KEY_LENGTH, _tt_record, ISEQUAL) == -1) { if (iserrno != ENOREC) { _tt_syslog(errstr, LOG_ERR, "%s: isstart(): %d", here, iserrno); return DM_ERROR; } // no record found so we are writing for the first time if (uid == (uid_t)-1) { // no record and we are not writing the uid return DM_ACCESS_DENIED; } if (_tt_uid == _TT_ROOT_UID) { // superuser can do anything } else if (_tt_uid != uid) { // Can only write yourself as owner return DM_ACCESS_DENIED; } oa = new _Tt_oid_access(key, uid, group, mode); isreclen = oa->reclen(); if (iswrite(isfd, oa->rec()) == -1) { return DM_ERROR; } } else { if (isread(isfd, _tt_record, ISNEXT) == -1) { _tt_syslog(errstr, LOG_ERR, "%s: isread(): %d", here, iserrno); return DM_ERROR; } oa = new _Tt_oid_access(_tt_record); uid_t owner = oa->user(); int valid_group = 0; for (int i = 0; i < _tt_gidlen; i++) { if (group == _tt_gidlist[i]) { valid_group = 1; break; } } if (_tt_uid == _TT_ROOT_UID) { // superuser can do anything } else if (uid != (uid_t)-1) { // can't change uid return DM_ACCESS_DENIED; } else if (group != (gid_t)-1 && (_tt_uid != owner || !valid_group)) { // only owner can change group and group must be valid return DM_ACCESS_DENIED; } else if (mode != (mode_t)-1 && _tt_uid != owner) { // only owner can change mode return DM_ACCESS_DENIED; } if (uid != (uid_t)-1) { oa->set_user(uid); } if (group != (gid_t)-1) { oa->set_group(group); } if (mode != (mode_t)-1) { oa->set_mode(mode); } isreclen = oa->reclen(); if (isrewrec(isfd, isrecnum, oa->rec()) == -1) { return DM_ERROR; } // Flush any cached access values _Tt_oid_access_ptr oa = _tt_oa_cache->lookup(key); if (! oa.is_null()) { _tt_oa_cache->remove(oa); } _Tt_link_access_ptr la = _tt_la_cache->lookup(key); if (! la.is_null()) { _tt_la_cache->remove(la); } } return DM_OK; } void _tt_update_modtime(int isfd, keydesc * key, int key_len, _Tt_isam_results & res) { static const char *here = "_tt_update_modtime()"; memset(_tt_record, 0, sizeof(_tt_record)); memcpy(_tt_record, key, key_len); res.result = isstart(isfd, key, key_len, _tt_record, ISEQUAL); if (res.result == -1) { if (iserrno != ENOREC && iserrno != EENDFILE) { _tt_syslog(errstr, LOG_ERR, "%s: isstart(): %d", here, iserrno); res.iserrno = iserrno; return; } // no mod time? res.result = 0; res.iserrno = 0; return; } res.result = isread(isfd, _tt_record, ISNEXT); if (res.result == -1) { if (iserrno != ENOREC && iserrno != EENDFILE) { _tt_syslog(errstr, LOG_ERR, "%s: isread(): %d", here, iserrno); res.iserrno = iserrno; return; } // no mod time? res.result = 0; res.iserrno = 0; return; } _Tt_string last_mod_time_string((const unsigned char *) &_tt_record[OID_KEY_LENGTH + MAX_PROP_LENGTH], isreclen - MAX_PROP_LENGTH - OID_KEY_LENGTH); long last_mod_time = atol((char *)last_mod_time_string); if (last_mod_time < 0) { last_mod_time = 0; } else { last_mod_time++; } char mod_string[16]; sprintf(&_tt_record[OID_KEY_LENGTH + MAX_PROP_LENGTH], "%ld", (long)last_mod_time); res.result = isrewcurr(isfd, _tt_record); res.iserrno = iserrno; return; } /* * _tt_min_auth_level_1 - returns the minimally required authentication level. */ int * _tt_min_auth_level_1(char ** /* file */, SVCXPRT * /* transp */) { return &_tt_auth_level; } /* * _tt_isaddindex_1 - wrapper for NetISAM isaddindex */ _Tt_isam_results * _tt_isaddindex_1(_Tt_isaddindex_args *args, SVCXPRT * /* transp */) { /* check for stale NetISAM file descriptor */ if (!_tt_check_stale_isfd(args->isfd)) { res.result = -1; res.iserrno = ERPC; } else { res.result = isaddindex(args->isfd, args->key); res.iserrno = iserrno; } return (&res); } /* * _tt_isbuild_1 - wrapper for NetISAM isbuild. The input path is in the form * :: */ _Tt_isam_results * _tt_isbuild_1(_Tt_isbuild_args *args, SVCXPRT * /* transp */) { static const char *here = "_tt_isbuild_1()"; _Tt_string db_path = (char *)NULL; mode_t prev_umask; isreclen = args->isreclen; prev_umask = umask(~(S_IRUSR+S_IWUSR)); // Only I can read and write db_path = map_old_db_to_new_db(args->path); res.result = isbuild(db_path, args->reclen, args->key, args->mode); umask(prev_umask); if (res.result == -1) { char *root_dir = (char *)db_path; int dir_len; if (root_dir) { dir_len = _Tt_dirname(root_dir); } else { dir_len = 0; } if (dir_len == 0 || ((iserrno != ENOENT) && (iserrno != EFNAME))) { // no root dir to build or strange error _tt_syslog(errstr, LOG_ERR, "%s: isbuild(): %d", here, iserrno); res.iserrno = iserrno; return (&res); } // failed because dir doesn't exist - create it char *dir = (char *)malloc(dir_len+2); memcpy(dir, root_dir, dir_len+1); dir[dir_len+1] = '\0'; /* * Create the TT_DIR directory with read and search (x) * permissions for everyone. Can't think of any harm that * could be done by allowing people to see the file names, * and it would be irritating to somebody poking around * looking for a problem not to be able to cd into the * directory. Setting the group search (x) permission is * particularly important, else if set-gid is set for the * directory (to get BSD semantics) ls shows the perms as * rwx--S---, and the capital S looks like a bug to most * people. */ if (mkdir(dir, S_IRWXU+S_IRGRP+S_IXGRP+S_IROTH+S_IXOTH) == -1) { _tt_syslog(errstr, LOG_ERR, "dir: %m", dir); free(dir); res.iserrno = iserrno; return (&res); } free(dir); if (db_path.is_null()) { res.iserrno = ERPC; _tt_syslog(errstr, LOG_ERR, "db_path.is_null()"); return (&res); } prev_umask = umask(~(S_IRUSR+S_IWUSR)); res.result = isbuild((char *)db_path, args->reclen, args->key, args->mode); umask(prev_umask); if (res.result == -1) { _tt_syslog(errstr, LOG_ERR, "%s: isbuild(): %d", here, iserrno); res.iserrno = iserrno; return (&res); } } _tt_db_table[res.result].db_path = db_path; _tt_db_table[res.result].opener_uid = _tt_uid; res.iserrno = iserrno; return (&res); } /* * _tt_isclose_1 - wrapper for NetISAM isclose */ _Tt_isam_results * _tt_isclose_1(int *isfd, SVCXPRT * /* transp */) { static const char *here = "_tt_isclose_1()"; if (!_tt_check_stale_isfd(*isfd)) { res.result = -1; res.iserrno = ERPC; _tt_syslog(errstr, LOG_ERR, "%s: _tt_check_stale_isfd() == 0", here ); } else { // JET - 06/12/2002 // VU#975403 - CERT TT vulnerability. By passing an invalid isfd // a local or remote attacker can zero out 4 bytes at any location, // thereby allowing other exploits (items 2 & 3 - delete or // overwrite any file on the system.) // Here, we will just check to make sure: 0 >= isfd < _TT_MAX_ISFD if (*isfd < 0 || *isfd >= _TT_MAX_ISFD) { // some trickery going on? res.result = -1; res.iserrno = ERPC; _tt_syslog(errstr, LOG_ERR, "%s: _tt_isclose_1: Invalid file descriptor. This may be an attempted exploit.", here ); } else { res.result = cached_isclose(*isfd); if (res.result != -1) { _tt_db_table[*isfd].db_path = 0; _tt_db_table[*isfd].opener_uid = (uid_t)-1; } else { _tt_syslog(errstr, LOG_ERR, "%s: isclose(): %d", here, iserrno); } res.iserrno = iserrno; } } return (&res); } /* * _tt_iscntl_1 - wrapper for NetISAM iscntl */ _Tt_iscntl_results * _tt_iscntl_1(_Tt_iscntl_args *args, SVCXPRT * /* transp */) { static const char *here = "_tt_iscntl_1()"; static _Tt_iscntl_results res; if (!_tt_check_stale_isfd(args->isfd)) { res.result = -1; res.iserrno = ERPC; _tt_syslog(errstr, LOG_ERR, "%s: _tt_check_stale_isfd() == 0", here ); } else { /* res.result = iscntl(args->isfd, args->func, args->arg.arg_val); res.iserrno = iserrno; res.arg.arg_val = args->arg.arg_val; res.arg.arg_len = args->arg.arg_len; if (res.result == -1) { _tt_syslog(errstr, LOG_ERR, "%s: iscntl(): %d", here, iserrno); } */ res.iserrno = 0; res.arg.arg_val = " 1"; res.arg.arg_len = 4; res.result = 0; } return (&res); } /* * _tt_isdelrec_1 - wrapper for NetISAM isdelrec */ _Tt_isam_results * _tt_isdelrec_1(_Tt_isdelrec_args *args, SVCXPRT *transp) { static const char *here = "_tt_isdelrec_1()"; if (!_tt_check_stale_isfd(args->isfd)) { res.result = -1; res.iserrno = ERPC; _tt_syslog(errstr, LOG_ERR, "%s: _tt_check_stale_isfd() == 0", here ); } else { if (args->rec.rec_len > 0) { if (_tt_oid_accessp(args->isfd, args->rec.rec_val, 'w')) { if (msg_q_lock(args->isfd, args->rec.rec_val, args->rec.rec_len, transp)) { res.result = isdelrec(args->isfd, args->recnum); res.iserrno = iserrno; } else { res.result = -1; res.iserrno = ERPC; } } else { res.result = -1; res.iserrno = DM_ACCESS_DENIED; } } else { res.result = -1; res.iserrno = ERPC; _tt_syslog(errstr, LOG_ERR, "%s: rec_len <= 0", here); } } return (&res); } /* * _tt_iserase_1 - wrapper for NetISAM iserase */ _Tt_isam_results * _tt_iserase_1(char **path, SVCXPRT * /* transp */) { res.result = iserase(*path); res.iserrno = iserrno; if (res.result == -1) { _tt_syslog(errstr, LOG_ERR, "iserase(): %d", iserrno); } return (&res); } /* * _tt_isopen_1 - wrapper for NetISAM isopen */ _Tt_isam_results * _tt_isopen_1(_Tt_isopen_args *args, SVCXPRT * /* transp */) { static const char *here = "_tt_isopen_1()"; _Tt_string db_path = args->path; if (db_path.is_null()) { res.iserrno = ERPC; _tt_syslog(errstr, LOG_ERR, "%s: args->path.is_null()", here); return (&res); } int prefix_len = _Tt_dirname((char *)db_path) + 1; if (prefix_len < 1) { res.iserrno = ERPC; _tt_syslog(errstr, LOG_ERR, "%s: _Tt_dirname(db_path) < 0", here); return (&res); } // bugid 1049947: someone could use our dbserver to get at // other isam files on the same machine. Protect (lightly) // against this by making sure "TT_DB" is in the path. // Someone could still bypass this by making symlinks to the // file he wants to get at, so the "right way" to fix this // would be to write a distinctive signature into the APPL_MAGIC // bucket, and check for that signature on open. However, // that's a more widespread change than we want to try to // squeeze in only hours from Beta... RFM 1/28/90 int l = db_path.len(); int tl = strlen("TT_DB"); int j; for (j = 0; jpath); if (db_path.is_null()) { res.iserrno = ERPC; _tt_syslog(errstr, LOG_ERR, "%s: map_old_db_to_new_db() == 0", here); return (&res); } res.result = cached_isopen((char *)db_path, args->mode); if (res.result != -1) { /* record database name and isfd */ _tt_db_table[res.result].db_path = db_path; _tt_db_table[res.result].opener_uid = _tt_uid; } else { // // Try to create the file. // The constructor create the files. // Then call isopen again, // the files will then exist. bug# 1179660 // _Tt_db_server_db * db; _tt_file_partition_results *partitionRes; char *file = (char *)db_path; partitionRes = _tt_get_file_partition_1(&file, NULL); db = new _Tt_db_server_db(partitionRes->partition); delete db; res.result = cached_isopen((char *)db_path, args->mode); if (res.result != -1) { /* record database name and isfd */ _tt_db_table[res.result].db_path = db_path; _tt_db_table[res.result].opener_uid = _tt_uid; } else { _tt_syslog(errstr, LOG_ERR, "%s: isopen(): %d", here, iserrno); } } res.iserrno = iserrno; return (&res); } /* * _tt_isread_1 - wrapper for NetISAM isread */ _Tt_isread_results * _tt_isread_1(_Tt_isread_args *args, SVCXPRT *transp) { static const char *here = "_tt_isread_1()"; static _Tt_isread_results res; if (!_tt_check_stale_isfd(args->isfd)) { res.isresult.result = -1; res.isresult.iserrno = ERPC; _tt_syslog(errstr, LOG_ERR, "%s: _tt_check_stale_isfd() == 0", here ); return (&res); } if (_tt_oid_accessp(args->isfd, args->rec.rec_val, 'r')) { isrecnum = args->isrecnum; memcpy(_tt_record, args->rec.rec_val, args->rec.rec_len); if (msg_q_lock(args->isfd, args->rec.rec_val, args->rec.rec_len, transp)) { res.isresult.result = isread(args->isfd, _tt_record, args->mode); #ifdef Foo // // Do not return the _MODIFICATIN_DATE information // to 4.x client systems (the only ones calling // this function). That is only in the file: // // TT_DB_PROPERTY_TABLE_FILE (table_fn below) // // NOTE: // //_MODIFICATION_DATE==TT_DB_PROPS_CACHE_LEVEL_PROPERTY. // // The record format is: // // ------------------------------ // | OID-KEY | PROPERTY | VALUE | // ------------------------------ // // and OID_KEY_LENGTH is the size of OID-KEY. // const char *table_fn = TT_DB_PROPERTY_TABLE_FILE; const char *mod_name=TT_DB_PROPS_CACHE_LEVEL_PROPERTY; // // Find the last slash in the full path name so // we can compare the file name (not dir names). // char *file = strrchr(_tt_db_table[args->isfd].db_path, '/'); if (file == NULL) { // Hack to avoid core dump if no '/' in name. file = _tt_db_table[args->isfd].db_path; } else { file++; // Point past '/' to file name. } if ((strncmp(table_fn, file, strlen(table_fn)) == 0) && (strncmp(mod_name, &_tt_record[OID_KEY_LENGTH], strlen(mod_name)) ==0)) { // // If you find a _MODIFICATION_DATE then, // Just return the next record or error. // return _tt_isread_1(args, transp); } else { #endif // Foo res.isresult.iserrno = iserrno; res.rec.rec_len = isreclen; res.rec.rec_val = _tt_record; #ifdef Foo } #endif } else { res.isresult.result = -1; res.isresult.iserrno = ERPC; } if (res.isresult.result == -1 && iserrno != EENDFILE) { _tt_syslog(errstr, LOG_ERR, "%s: isread(): %d", here, iserrno); } } else { res.isresult.result = -1; res.isresult.iserrno = DM_ACCESS_DENIED; res.rec.rec_len = 0; res.rec.rec_val = 0; } res.isreclen = isreclen; res.isrecnum = isrecnum; return (&res); } /* * _tt_isrewrec_1 - wrapper for NetISAM isrewrec */ _Tt_isam_results * _tt_isrewrec_1(_Tt_isrewrec_args *args, SVCXPRT *transp) { static const char *here = "_tt_isrewrec_1()"; if (!_tt_check_stale_isfd(args->isfd)) { res.result = -1; res.iserrno = ERPC; _tt_syslog(errstr, LOG_ERR, "%s: _tt_check_stale_isfd() == 0", here ); } else { if (args->rec.rec_len > 0) { if (_tt_oid_accessp(args->isfd, args->rec.rec_val, 'w')) { if (msg_q_lock(args->isfd, args->rec.rec_val, args->rec.rec_len, transp)) { isreclen = args->rec.rec_len; res.result = isrewrec(args->isfd, args->recnum, args->rec.rec_val); res.iserrno = iserrno; if (res.result == -1) { _tt_syslog(errstr, LOG_ERR, "%s: isrewrec" "(): %d", here, iserrno); } } else { res.result = -1; res.iserrno = ERPC; } } else { res.result = -1; res.iserrno = DM_ACCESS_DENIED; } } else { res.result = -1; res.iserrno = ERPC; _tt_syslog(errstr, LOG_ERR, "%s: rec_len <= 0", here); } } return (&res); } /* * _tt_isstart_1 - wrapper for NetISAM isstart */ _Tt_isam_results * _tt_isstart_1(_Tt_isstart_args *args, SVCXPRT *transp) { static const char *here = "_tt_isstart_1"; if (!_tt_check_stale_isfd(args->isfd)) { res.result = -1; res.iserrno = ERPC; _tt_syslog(errstr, LOG_ERR, "%s: _tt_check_stale_isfd() == 0", here ); } else if (args->rec.rec_val==0 && args->mode!=ISFIRST) { res.result = -1; res.iserrno = EBADKEY; _tt_syslog(errstr, LOG_ERR, "rec_val == 0 && mode != ISFIRST"); } else if (msg_q_lock(args->isfd, args->rec.rec_val, args->rec.rec_len, transp)) { res.result = isstart(args->isfd, args->key, args->key_len, args->rec.rec_val, args->mode); res.iserrno = iserrno; if (res.result == -1) { // ENOREC happens a lot even in normal operation. if (iserrno!=ENOREC) { _tt_syslog(errstr, LOG_ERR, "%s: isstart(): %d", here, iserrno); } } } else { res.result = -1; res.iserrno = ERPC; } return (&res); } /* * _tt_iswrite_1 - wrapper for NetISAM iswrite */ _Tt_isam_results * _tt_iswrite_1(_Tt_iswrite_args *args, SVCXPRT *transp) { static const char *here = "_tt_iswrite_1()"; if (!_tt_check_stale_isfd(args->isfd)) { res.result = -1; res.iserrno = ERPC; _tt_syslog(errstr, LOG_ERR, "%s: _tt_check_stale_isfd() == 0", here ); } else { if (args->rec.rec_len > 0) { if (_tt_oid_accessp(args->isfd, args->rec.rec_val, 'w')) { if (msg_q_lock(args->isfd, args->rec.rec_val, args->rec.rec_len, transp)) { isreclen = args->rec.rec_len; res.result = iswrite(args->isfd, args->rec.rec_val); res.iserrno = iserrno; if (res.result == -1) { _tt_syslog(errstr, LOG_ERR, "%s: iswrite" "(): %d", here, iserrno); } } else { res.result = -1; res.iserrno = ERPC; } } else { res.result = -1; res.iserrno = DM_ACCESS_DENIED; } } else { res.result = -1; res.iserrno = ERPC; } } return (&res); } /* * _tt_test_and_set_1 - reads (test) the record with the given input * (key + prop name). If the record already existed, returns it and 0. * Otherwise, writes (set) the given record out and returns 1. If any error, * returns -1. */ _Tt_test_and_set_results * _tt_test_and_set_1(_Tt_test_and_set_args *args, SVCXPRT *transp) { static const char *here = "_tt_test_and_set_1()"; static _Tt_test_and_set_results res; if (!_tt_check_stale_isfd(args->isfd)) { res.isresult.result = -1; res.isresult.iserrno = ERPC; _tt_syslog(errstr, LOG_ERR, "%s: _tt_check_stale_isfd() == 0", here ); return (&res); } if (!_tt_oid_accessp(args->isfd, args->rec.rec_val, 'r' + 'w')) { res.isresult.result = -1; res.isresult.iserrno = DM_ACCESS_DENIED; res.rec.rec_len = 0; res.rec.rec_val = 0; return (&res); } if (!msg_q_lock(args->isfd, args->rec.rec_val, args->rec.rec_len, transp)) { res.isresult.result = -1; res.isresult.iserrno = ERPC; res.rec.rec_len = 0; res.rec.rec_val = 0; return (&res); } memcpy(_tt_record, args->rec.rec_val, args->rec.rec_len); // look for this record if (isstart(args->isfd, args->key, args->key_len, _tt_record, ISEQUAL) == -1) { if (iserrno == ENOREC) { // not found, we can write ours isreclen = args->rec.rec_len; if (iswrite(args->isfd, args->rec.rec_val) != -1) { res.isresult.result = 1; res.rec.rec_len = 0; res.rec.rec_val = 0; res.isrecnum = isrecnum; return (&res); } _tt_syslog(errstr, LOG_ERR, "%s: iswrite(): %d", here, iserrno); res.isresult.iserrno = iserrno; res.isresult.result = -1; res.rec.rec_len = 0; res.rec.rec_val = 0; return (&res); } else { _tt_syslog(errstr, LOG_ERR, "%s: isstart(): %d", here, iserrno); res.isresult.iserrno = iserrno; res.isresult.result = -1; res.rec.rec_len = 0; res.rec.rec_val = 0; return (&res); } } // we found a record, read it and return it if (isread(args->isfd, _tt_record, ISNEXT) != -1) { res.isresult.result = 0; res.isresult.iserrno = iserrno; res.rec.rec_len = isreclen; res.rec.rec_val = _tt_record; res.isrecnum = isrecnum; return (&res); } // EENDFILE happens a lot even in normal operation. if (iserrno && iserrno!=EENDFILE) { _tt_syslog(errstr, LOG_ERR, "%s: isread(): %d", here, iserrno); } res.isresult.iserrno = iserrno; res.isresult.result = -1; res.rec.rec_len = 0; res.rec.rec_val = 0; return (&res); } /* * _tt_transaction_1 - given a list of records, writes them out to the log file * associated with the given database. If the log file writes succeed returns * 0, otherwise returns -1. The actual updating of database records is done * by procedure _tt_process_transaction and is invoked by _tt_dbserver_prog_1 * after a reply is sent to the client. In case where there's a crash, the * server recovers by checking for the existents of log files every time it * opens a database and invoking _tt_process_transaction. The log file is in * the same directory as that of the database. The log file's name is * "log_file". * * The log file has the following format: * * * , * where each element of the set is * */ _Tt_isam_results * _tt_transaction_error(int fd) { if (fd != -1) { close(fd); unlink(_tt_log_file); _tt_log_file[0] = '\0'; } res.result = -1; return (&res); } _Tt_isam_results * _tt_transaction_1(_Tt_transaction_args* args, SVCXPRT * /* transp */) { struct stat buf; // JET - VU#975403/VU#299816 static const char *here = "_tt_transaction_1()"; /* check for stale NetISAM file descriptor */ if (!_tt_check_stale_isfd(args->isfd)) { res.iserrno = ERPC; _tt_syslog(errstr, LOG_ERR, "%s: _tt_check_stale_isfd() == 0", here ); return _tt_transaction_error(args->isfd); } _Tt_trans_record *trec = args->recs; if (!trec) { res.iserrno = ERPC; return _tt_transaction_error(-1); } int fd = -1; _tt_log_file[0] = '\0'; /* Must have write permission to start transaction */ if (_tt_oid_accessp(args->isfd, trec->rec.rec_val, 'w')) { char *db_path = _tt_db_table[args->isfd].db_path; if (!db_path) { res.iserrno = ERPC; return _tt_transaction_error(fd); } int prefix_len = _Tt_dirname(db_path) + 1; if (prefix_len < 1) { res.iserrno = ERPC; return _tt_transaction_error(fd); } memcpy(_tt_log_file, db_path, prefix_len); strcpy(_tt_log_file+prefix_len, _TT_LOG_FILE); if (access(_tt_log_file, F_OK) == 0) { _tt_process_transaction(); } // JET - 06/24/2002 VU#975403/VU#299816 - CERT TT // vulnerability. check for the presence of a // symlink. Abort (nicely) if there. if(lstat(_tt_log_file, &buf) != -1) { // present if (S_ISLNK(buf.st_mode)) { // it's a symlink. Oops. _tt_syslog(errstr, LOG_ERR, "%s: _tt_log_file is a symlink. Aborting.", here ); res.result = -1; res.iserrno = DM_ACCESS_DENIED; return(&res); } } if ((fd = open(_tt_log_file, O_RDWR | O_CREAT, S_IREAD + S_IWRITE)) == -1) { res.iserrno = DM_WRITE_FAILED; return _tt_transaction_error(fd); } /* Turn on close-on-exec */ fcntl(fd, F_SETFD, 1); /* reset to beginning of file */ off_t offset; if ((offset = lseek(fd, 0, SEEK_SET)) == -1) { res.iserrno = DM_WRITE_FAILED; return _tt_transaction_error(fd); } /* set the transaction flag to signify begin of transaction */ int flag = 1; if (write(fd, (char *)&flag, sizeof(int)) == -1) { res.iserrno = DM_WRITE_FAILED; return _tt_transaction_error(fd); } /* write the target database path out to the log file */ if (write(fd, db_path, strlen(db_path)+1) == -1) { res.iserrno = DM_WRITE_FAILED; return _tt_transaction_error(fd); } /* write the records out to the log file */ while (trec) { /* write the record's new flag out to the log file */ if (write(fd, (char *)&trec->newp, sizeof(int)) == -1) { res.iserrno = DM_WRITE_FAILED; return _tt_transaction_error(fd); } /* write the record number out to the log file */ if (write(fd, (char *)&trec->recnum, sizeof(long)) == -1) { res.iserrno = DM_WRITE_FAILED; return _tt_transaction_error(fd); } /* write the record's length out to the log file */ if (write(fd, (char *)&trec->rec.rec_len, sizeof(u_int)) == -1) { res.iserrno = DM_WRITE_FAILED; return _tt_transaction_error(fd); } /* write the record's content out to the log file */ if (write(fd, (char *)trec->rec.rec_val, trec->rec.rec_len) == -1) { res.iserrno = DM_WRITE_FAILED; return _tt_transaction_error(fd); } trec = trec->next; } if (DM_TEST_CRASH) { /* Test crash recovery */ fprintf(stderr,"_tt_transaction_1: simulating server crash to test crash recovery . . . exiting\n"); exit(1); } /* clear the transaction flag to signify transaction commit */ if (lseek(fd, 0, SEEK_SET) == -1) { res.iserrno = DM_WRITE_FAILED; return _tt_transaction_error(fd); } flag = 0; if (write(fd, (char *)&flag, sizeof(int)) == -1) { res.iserrno = DM_WRITE_FAILED; return _tt_transaction_error(fd); } /* close the log file */ if (close(fd) == -1) { res.iserrno = DM_WRITE_FAILED; return _tt_transaction_error(fd); } res.result = 0; } else { res.result = -1; res.iserrno = DM_ACCESS_DENIED; } return (&res); } /* * _tt_mfs_1 - find a mount entry whose mounted directory/file system is the * longest match of the given directory/absolute path. For example, the mount * entry "/home3" matches "/home3/dynamo" better than the mount entry "/". * If msfp == 1, then match using directory. Otherwise, match using absolute * path. */ char ** _tt_mfs_1(char **path, SVCXPRT * /* transp */) { static char *res; if (!*path) { res = ""; } else { _Tt_file_system fs; _Tt_file_system_entry_ptr fsep = fs.bestMatchToPath(*path); res = fsep->getMountPoint(); } return (&res); } /* * _tt_getoidaccess_1 - get the uid/group/mode for an oid */ _Tt_oidaccess_results * _tt_getoidaccess_1(_Tt_oidaccess_args *args, SVCXPRT * /* transp */) { static _Tt_oidaccess_results res; res.result = _tt_read_oid_user(args->isfd, args->key.key_val, res.uid, res.group, res.mode); res.iserrno = iserrno; return (&res); } /* * _tt_setoiduser_1 - set the user for an oid */ _Tt_isam_results * _tt_setoiduser_1(_Tt_oidaccess_args *args, SVCXPRT * /* transp */) { res.result = _tt_write_oid_access(args->isfd, args->key.key_val, (uid_t)args->value, (gid_t)-1, (mode_t)-1); res.iserrno = iserrno; return (&res); } /* * _tt_setoidgroup_1 - set the user for an oid */ _Tt_isam_results * _tt_setoidgroup_1(_Tt_oidaccess_args *args, SVCXPRT * /* transp */) { res.result = _tt_write_oid_access(args->isfd, args->key.key_val, (uid_t)-1, (gid_t)args->value, (mode_t)-1); res.iserrno = iserrno; return (&res); } /* * _tt_setoidmode_1 - set the user for an oid */ _Tt_isam_results * _tt_setoidmode_1(_Tt_oidaccess_args *args, SVCXPRT * /* transp */) { res.result = _tt_write_oid_access(args->isfd, args->key.key_val, (uid_t)-1, (gid_t)-1, (mode_t)args->value); res.iserrno = iserrno; return (&res); } _Tt_spec_props * _tt_readspec_1(_Tt_spec_props * /* argp */, SVCXPRT * /* transp */) { static _Tt_spec_props res; res.iserrno = iserrno; return (&res); } _Tt_isam_results * _tt_writespec_1(_Tt_spec_props * /* argp */, SVCXPRT * /* transp */) { res.iserrno = iserrno; return (&res); } _Tt_isam_results * _tt_addsession_1(_Tt_session_args *argp, SVCXPRT * /* transp */) { static const char *here = "_tt_addsession_1()"; Table_oid_prop* tp = (Table_oid_prop *) _tt_record; _Tt_string sessionid((unsigned char *)argp->session.session_val, argp->session.session_len); int isfd = argp->isfd; _Tt_string filejoin_prop(_TT_FILEJOIN_PROPNAME); if (!_tt_check_stale_isfd(isfd)) { res.result = -1; res.iserrno = ERPC; _tt_syslog(errstr, LOG_ERR, "%s: _tt_check_stale_isfd() == 0", here ); } // Start reading session props memset(_tt_record, 0, sizeof(_tt_record)); memcpy(_tt_record, argp->key, argp->key_len); res.result = isstart(isfd, argp->key, argp->key_len, _tt_record, ISEQUAL); res.iserrno = iserrno; if (res.result == -1) { if (iserrno != ENOREC && iserrno != EENDFILE) { _tt_syslog(errstr, LOG_ERR, "%s: isstart(): %d", here, iserrno); return (&res); } // no record found, no sessions on this doc node } else { // check to make sure this session is not already registered for (;;) { res.result = isread(isfd, _tt_record, ISNEXT); if (res.result == -1) { if (iserrno != ENOREC && iserrno != EENDFILE) { _tt_syslog(errstr, LOG_ERR, "%s: isread" "(): %d", here, iserrno); return (&res); } break; } if (strcmp((char *) filejoin_prop, tp->propname)) { // ran past the session prop break; } if (memcmp(argp->oidkey.oidkey_val, tp->objkey, argp->oidkey.oidkey_len)) { // ran past our key break; } if (memcmp((char *) sessionid, tp->propval, isreclen-MAX_PROP_LENGTH-OID_KEY_LENGTH)) { // already here so we're done res.result = 0; return (&res); } } } // need to add this session to the doc node // zero out _tt_record, which is where tp points memset(_tt_record, 0, sizeof(_tt_record)); memcpy(tp->objkey, argp->oidkey.oidkey_val, argp->oidkey.oidkey_len); strcpy(tp->propname, filejoin_prop); memcpy(tp->propval, (char *) sessionid, sessionid.len()); isreclen = OID_KEY_LENGTH+MAX_PROP_LENGTH+sessionid.len(); res.result = iswrite(isfd, _tt_record); if (res.result == -1) { _tt_syslog(errstr, LOG_ERR, "%s: iswrite(): %d", here, iserrno); res.iserrno = iserrno; return (&res); } _tt_update_modtime(isfd, argp->key, argp->key_len, res); return (&res); } _Tt_isam_results * _tt_delsession_1(_Tt_session_args *argp, SVCXPRT * /* transp */) { static const char *here = "_tt_delsession_1()"; Table_oid_prop* tp = (Table_oid_prop *) _tt_record; _Tt_string sessionid((unsigned char *)argp->session.session_val, argp->session.session_len); int isfd = argp->isfd; _Tt_string filejoin_prop(_TT_FILEJOIN_PROPNAME); if (!_tt_check_stale_isfd(isfd)) { res.result = -1; res.iserrno = ERPC; _tt_syslog(errstr, LOG_ERR, "%s: _tt_check_stale_isfd() == 0", here ); } // Start reading session props memset(_tt_record, 0, sizeof(_tt_record)); memcpy(_tt_record, argp->key, argp->key_len); res.result = isstart(isfd, argp->key, argp->key_len, _tt_record, ISEQUAL); res.iserrno = iserrno; if (res.result == -1) { // // // _tt_syslog(errstr, LOG_ERR, "%s: isstart(): %d", here, iserrno); return (&res); } // look for this session for (;;) { res.result = isread(isfd, _tt_record, ISNEXT); if (res.result == -1) { _tt_syslog(errstr, LOG_ERR, "%s: isread(): %d", here, iserrno); break; } if (strcmp((char *) filejoin_prop, tp->propname)) { // ran past the session prop break; } if (memcmp(argp->oidkey.oidkey_val, tp->objkey, argp->oidkey.oidkey_len)) { // ran past our key break; } if (memcmp((char *) sessionid, tp->propval, isreclen-MAX_PROP_LENGTH-OID_KEY_LENGTH)) { // we found it, now delete it res.result = isdelcurr(isfd); if (res.result == -1) { _tt_syslog(errstr, LOG_ERR, "%s: isdelcurr" "(): %d", here, iserrno); res.iserrno = iserrno; return (&res); } break; } } _tt_update_modtime(isfd, argp->key, argp->key_len, res); return (&res); } _Tt_spec_props * _tt_gettype_1(_Tt_spec_props *argp, SVCXPRT * /* transp */) { static const char *here = "_tt_gettype_1()"; static _Tt_spec_props res; static _Tt_prop props; _Tt_string propname((unsigned char *)argp->props.props_val[0].propname.propname_val, argp->props.props_val[0].propname.propname_len); int isfd = argp->isfd; if (!_tt_check_stale_isfd(isfd)) { res.result = -1; res.iserrno = ERPC; _tt_syslog(errstr, LOG_ERR, "%s: _tt_check_stale_isfd() == 0", here ); } memset(_tt_record, 0, sizeof(_tt_record)); memcpy(_tt_record, argp->key, argp->key_len); res.result = isstart(isfd, argp->key, argp->key_len, _tt_record, ISEQUAL); res.iserrno = iserrno; if (res.result == -1) { _tt_syslog(errstr, LOG_ERR, "%s: isstart(): %d", here, iserrno); return (&res); } res.result = isread(isfd, _tt_record, ISNEXT); if (res.result == -1) { _tt_syslog(errstr, LOG_ERR, "%s: isread(): %d", here, iserrno); return (&res); } res.props.props_len = 1; res.props.props_val = &props; props.value.value_val = &_tt_record[OID_KEY_LENGTH+MAX_PROP_LENGTH]; props.value.value_len = isreclen-MAX_PROP_LENGTH-OID_KEY_LENGTH; props.recnum = 0; props.propname.propname_val = 0; props.propname.propname_len = 0; res.iserrno = iserrno; return (&res); } // // Go through the _tt_db_table and check on each file. // Then compresse the files (empty records deleted). // void isgarbage_collect() { register int offset; _Tt_string dbName; for (offset = 0; offset < _TT_MAX_ISFD; offset++) { #ifdef OPT_GARBAGE_THREADS // // Allow an RPC process to run until it exits. // UNLOCK_RPC(); thr_yield(); LOCK_RPC(); #endif // Don't mess with open files! if (_tt_db_table[offset].db_path.len() > 0) { // // Save name as cached_isclose() removes the // name from the table. // dbName = _tt_db_table[offset].db_path; cached_isclose(offset); isgarbage(_tt_db_table[offset].db_path); } } return; } int cached_isopen(const char *filepath, int mode) { // First search for an available open isfd for this file, // reuse it if found. int i, isfd, lru_fd; _Tt_string fp(filepath); _Tt_db_info *p; for (i=0; i<_TT_MAX_ISFD; i++) { p = _tt_db_table+i; if (!p->client_has_open && p->db_path == fp && p->open_mode == mode) { p->client_has_open = 1; p->reftime = _tt_refclock++; return i; } } // No suitable reusable isfd was found. Try opening a new one. // If we can't open one because they're all in use (ETOOMANY), // close the least-recently-used one and try opening again. for (;;) { isfd = isopen(filepath, mode); if (isfd!=-1) { // Opened fine. p = _tt_db_table+isfd; p->db_path = fp; p->open_mode = mode; p->client_has_open = 1; p->server_has_open = 1; p->reftime = _tt_refclock++; return isfd; } else if (iserrno!=ETOOMANY) { // some REAL problem.. // return isfd; } // find the least-recently-used isfd and close it. // Note that any fd a client has open is considered // more recently used than any fd a client doesn't have // open; this prevents us from giving away fd's a client // thinks it has open, unless we're really in desparate // straits. It would be nice to completely decouple the // "fd" numbers we hand back from the isfd's, so that // stale fd's could be more reliably distinguished. lru_fd = -1; for (i = 0; i<_TT_MAX_ISFD; i++) { if (!_tt_db_table[i].server_has_open) { // we never opened this one, so we better // not close it. continue; } if (lru_fd < 0) { lru_fd = i; } if (_tt_db_table[i].reftime< _tt_db_table[lru_fd].reftime) { // this entry is older, check open status if (_tt_db_table[lru_fd].client_has_open || !_tt_db_table[i].client_has_open) { lru_fd = i; } } else { // this entry is newer, but still check // open status if (_tt_db_table[lru_fd].client_has_open && !_tt_db_table[i].client_has_open) { lru_fd = i; } } } isclose(lru_fd); // mark cache entry invalid in case open fails. _tt_db_table[lru_fd].client_has_open = 0; _tt_db_table[lru_fd].server_has_open = 0; _tt_db_table[lru_fd].db_path = ""; } } int cached_isclose(int isfd) { int isrc; if (-1 == isfd) return ISERROR; if (!_tt_db_table[isfd].server_has_open) { // The open was not cached (perhaps because it was an // isbuild instead of an isopen.) So do a real // close. isrc = isclose(isfd); // mark cache entry invalid (it probably was already.) _tt_db_table[isfd].client_has_open = 0; _tt_db_table[isfd].server_has_open = 0; _tt_db_table[isfd].db_path = ""; return isrc; } // Ensure everything gets written out. isrc = isfsync(isfd); // Mark cache entry as reusable _tt_db_table[isfd].client_has_open = 0; return isrc; } int find_endstring(const char *string, const char *end_string) { int sl = strlen(string); int esl = strlen(end_string); if (sl >= esl) { if (!strncmp(string+sl-esl, end_string, esl)) { return sl-esl; } } return -1; } // See if the message queue is locked or should be locked... // // Args: file descriptor, record buffer, record length, RPC client info // bool_t msg_q_lock(int isfd, const char *record, int length, SVCXPRT *transp) { _Tt_string db_file = _tt_db_table [isfd].db_path; // If this is not the property table then we don't care about it if (!db_file.sh_match("*property_table")) { return TRUE; } // See if this is a _TT_QUEUED_MSGS property access... // (with a 16 byte key, the rec length must be at least 31) if ((length > 30) && !memcmp(record+16, "_TT_QUEUED_MSGS", 15)) { // Get the address of the client #if defined(OPT_TLI) netbuf *client_address = svc_getrpccaller(transp); _Tt_string client_id(client_address->len); memcpy((char *)client_id, client_address->buf, client_address->len); #else struct sockaddr_in *client_address = svc_getcaller(transp); _Tt_string client_id(sizeof(struct in_addr)); memcpy( (char *)client_id, &client_address->sin_addr, sizeof(struct in_addr) ); #endif // Get the file key _Tt_string file_key(16); (void)memcpy((char *)file_key, record, 16); // Set a lock for this client and file key and lock the message queue // for new DB clients _Tt_db_msg_q_lock lock; return lock.testAndSetLock(client_id, file_key); } // Else, see if this is a _TT_MSG__ property access // (with a 16 byte key, the rec length must be at least 27) else if ((length > 26) && !memcmp(record+16, "_TT_MSG_", 8)) { // Get the address of the client #if defined(OPT_TLI) netbuf *client_address = svc_getrpccaller(transp); _Tt_string client_id(client_address->len); (void)memcpy((char *)client_id, client_address->buf, client_address->len); #else struct sockaddr_in *client_address = svc_getcaller(transp); _Tt_string client_id(sizeof(struct in_addr)); memcpy( (char *)client_id, &client_address->sin_addr, sizeof(struct in_addr) ); #endif // Get the file key _Tt_string file_key(16); (void)memcpy((char *)file_key, record, 16); // Unlock the message queue for new DB clients _Tt_db_msg_q_lock lock; lock.unsetLock(client_id, file_key); } return TRUE; } _Tt_string map_old_db_to_new_db(const char *old_db) { _Tt_string new_db(old_db); int index; if ((index = find_endstring(old_db, _TT_DOCOID_PATH)) > 0) { new_db = get_new_db(new_db, index, TT_DB_FILE_TABLE_FILE); } else if ((index = find_endstring(old_db, _TT_OID_CONTAINER)) > 0) { new_db = get_new_db(new_db, index, TT_DB_FILE_OBJECT_MAP_FILE); } else if ((index = find_endstring(old_db, _TT_OID_PROP)) > 0) { new_db = get_new_db(new_db, index, TT_DB_PROPERTY_TABLE_FILE); } else if ((index = find_endstring(old_db, _TT_OID_ACCESS)) > 0) { new_db = get_new_db(new_db, index, TT_DB_ACCESS_TABLE_FILE); } else { new_db = (char *)NULL; } return new_db; } // Takes the old DB path and table name, the index of where the table name // starts in the path and the new table name to replace the old name // with. It returns the path with the new table name. This function also // handles the renaming of the old DB table to the new name and the change // of format in the access table. _Tt_string get_new_db(_Tt_string old_db, int index, _Tt_string new_file) { static bool_t old_db_diagnostic = FALSE; _Tt_string new_db(old_db); new_db = new_db.left(index); if (new_db [new_db.len()-1] != '/') { new_db = new_db.cat("/"); } new_db = new_db.cat(new_file); _Tt_string new_db_index(new_db); new_db_index = new_db_index.cat(".ind"); _Tt_string old_db_index(old_db); old_db_index = old_db_index.cat(".ind"); struct stat stat_buf; int temp_errno = 0; // If there is no new DB and there is an old DB... if (stat((char *)new_db_index, &stat_buf) && ((temp_errno = errno) == ENOENT) && !stat((char *)old_db_index, &stat_buf)) { // If the file being accessed is the access_table... if (new_file == TT_DB_ACCESS_TABLE_FILE) { _Tt_isam_file_ptr old_db_table = new _Tt_isam_file(old_db, ISFIXLEN+ISINOUT+ISEXCLLOCK); _Tt_isam_file_ptr access_table; int results = old_db_table->getErrorStatus(); if (!results) { _Tt_isam_key_descriptor_ptr access_key = new _Tt_isam_key_descriptor; access_key->addKeyPart(0, TT_DB_KEY_LENGTH, BINTYPE); access_table = new _Tt_isam_file(new_db, TT_DB_KEY_LENGTH+3*TT_DB_LONG_SIZE, TT_DB_KEY_LENGTH+3*TT_DB_LONG_SIZE, access_key, ISFIXLEN+ISINOUT+ISEXCLLOCK); (void)access_table->writeMagicString(_Tt_string(TT_DB_VERSION)); results = access_table->getErrorStatus(); } if (!results) { _Tt_isam_record_ptr new_record = access_table->getEmptyRecord(); _Tt_isam_record_ptr record; while (!results) { record = old_db_table->readRecord(ISNEXT); results = old_db_table->getErrorStatus(); if (!results) { memset((char *)new_record->getRecord(), '\0', new_record->getLength()); memcpy((char *)new_record->getRecord(), (char *)record->getRecord(), TT_DB_KEY_LENGTH); short n_user = *(short *) ((char *)record->getRecord()+TT_DB_KEY_LENGTH); long user = (long)ntohs(n_user); u_long nl_user = htonl(user); memcpy((char *)new_record->getRecord()+TT_DB_ACCESS_USER_OFFSET, (char *)&nl_user, TT_DB_LONG_SIZE); short n_group = *(short *) ((char *)record->getRecord()+ TT_DB_KEY_LENGTH+TT_DB_SHORT_SIZE); long group = (long)ntohs(n_group); u_long nl_group = htonl(group); memcpy((char *)new_record->getRecord()+TT_DB_ACCESS_GROUP_OFFSET, (char *)&nl_group, TT_DB_LONG_SIZE); short n_mode = *(short *) ((char *)record->getRecord()+ TT_DB_KEY_LENGTH+2*TT_DB_SHORT_SIZE); long mode = (long)ntohs(n_mode); u_long nl_mode = htonl(mode); memcpy((char *)new_record->getRecord()+TT_DB_ACCESS_MODE_OFFSET, (char *)&nl_mode, TT_DB_LONG_SIZE); results = access_table->writeRecord(new_record); } } if (results == EENDFILE) { results = 0; } } if (!results) { (void)iserase((char *)old_db); } if (results) { new_db = (char *)NULL; } } // Else, everything other than the access table is just renamed... else { if (isrename((char *)old_db, (char *)new_db)) { new_db = (char *)NULL; } if (!new_db.is_null()) { _Tt_isam_file_ptr new_db_table = new _Tt_isam_file(old_db, ISFIXLEN+ISINOUT+ISEXCLLOCK); int results = new_db_table->getErrorStatus(); if (results) { new_db_table = new _Tt_isam_file(old_db, ISVARLEN+ISINOUT+ISEXCLLOCK); results = new_db_table->getErrorStatus(); } if (!results) { (void)new_db_table->writeMagicString(TT_DB_VERSION); } } } } // The stat on the new DB generated a nasty error... else if (temp_errno && (temp_errno != ENOENT)) { new_db = (char *)NULL; } // Old and new DB files exist... else if (!stat((char *)old_db_index, &stat_buf)) { if (old_db_diagnostic == FALSE) { _tt_syslog(errstr, LOG_ERR, catgets(_ttcatd, 5, 4, "Any data written using an old (<= 1.0.2) " "rpc.ttdbserverd after using a new (>= 1.1) " "rpc.ttdbserverd will be ignored")); old_db_diagnostic = TRUE; } } return new_db; }