/* * 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 libraries 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. */ /*%% $XConsortium: isfcb.c /main/3 1995/10/23 11:38:34 rswiston $ */ /* * Copyright (c) 1988 by Sun Microsystems, Inc. */ /* * isfcb.c * * Description: _ambuild() * File Control Block functions * * */ #include #include #include "isam_impl.h" #include static int _create_datfile(), _create_indfile(), _create_varfile(); static void _remove_datfile(), _remove_indfile(), _remove_varfile(); Static int _open_datfile(), _open_indfile(), _open_varfile(); /* * _isfcb_create(isfname, crdat, crind, crvar, owner, group, u_mask, errcode) * * Create ISAM file: create UNIX files (dat/ind/var), * and initialize File Control Block. * * Return 0 if create is successful, or -1 if any error. In the case of *an error, the errcode block is set. * * crdat, 0/1 flag * crind, 0/1 flag * crvar, 0/1 flag */ Fcb * _isfcb_create(char *isfname, int crdat, int crind, int crvar, int owner, int group, int u_mask, struct errcode *errcode) { Fcb *fcb; int dat_fd = -1; int ind_fd = -1; int var_fd = -1; int oldumask = umask (u_mask); /* Change umask to client's */ /* * Create the UNIX file for .rec file. */ if (crdat && (dat_fd = _create_datfile (isfname)) == -1) { _amseterrcode(errcode, errno); goto ERROR; } /* * If a primary is specified, create .ind file. */ if (crind && (ind_fd = _create_indfile (isfname)) == -1) { _amseterrcode(errcode, errno); goto ERROR; } /* * If the ISAM file is for variable length records, create .var file. */ if (crvar && (var_fd = _create_varfile (isfname)) == -1) { _amseterrcode(errcode, errno); goto ERROR; } /* * Change user and group onwer ship of the file. * This has affect only when executed by the netisamd daemon. */ if (dat_fd != -1) (void) fchown(dat_fd, owner, group); if (ind_fd != -1) (void) fchown(ind_fd, owner, group); if (var_fd != -1) (void) fchown(var_fd, owner, group); /* * Allocate File Control Block. */ fcb = (Fcb *) _ismalloc(sizeof(*fcb)); memset ((char *) fcb, 0, sizeof(*fcb)); fcb->isfname = _isallocstring(isfname); fcb->rdonly = FALSE; fcb->datfd = dat_fd; fcb->indfd = ind_fd; fcb->varfd = var_fd; fcb->datsize = N_CNTL_PAGES; /* Control Pages */ fcb->indfreelist = FREELIST_NOPAGE; /* Key descriptor */ fcb->nkeys = 1; fcb->keys = (Keydesc2 *) _ismalloc(sizeof(fcb->keys[0])); memset((char *)fcb->keys, 0, sizeof(fcb->keys[0])); return (fcb); ERROR: /* Undo whatever was done. */ if (dat_fd != -1) { (void) close(dat_fd); _remove_datfile(isfname); } if (ind_fd != -1) { (void) close(ind_fd); _remove_indfile(isfname); } if (var_fd != -1) { (void) close(var_fd); _remove_varfile(isfname); } (void) umask(oldumask); return (NULL); } /* * _isfcb_setlength(fcb, varflag, minreclen, maxreclen) * * Set FCB attributes pertaining to record length. */ void _isfcb_setreclength(Fcb *fcb, Bool varflag, int minreclen, int maxreclen) { fcb->varflag = varflag; fcb->minreclen = minreclen; fcb->maxreclen = maxreclen; } /* * _isfcb_open(isfname, errcode) * * Open ISAM file: open UNIX files and create File Control Block. * * Return 0 if open is successful, or -1 if any error. In the case of an error, * the errcode block is set. * * Note that rdonly is not fuly implemented. Now, all ISAM files are assumed * to have permissions set to 0666 and may be opened in RW mode. * If read-only media are used then the _open_datfile() function would have to * try first to open the file in RW mode, and of that failed, try to * open it in RO mode. The rdonly flag is used to reject any isamopen() with * INOUT or OUTPUT mode on such files. */ Fcb * _isfcb_open(char *isfname, struct errcode *errcode) { Fcb *fcb; int dat_fd = -1; int ind_fd = -1; int var_fd = -1; Bool rdonly; /* set to 1 if file is Read-Only */ /* * Open the UNIX file for .rec file. */ if ((dat_fd = _open_datfile (isfname, &rdonly)) == -1 || errno == EMFILE) { _amseterrcode(errcode, errno); if(dat_fd != -1) { close(dat_fd); } return (NULL); } /* * Open .ind file. */ ind_fd = _open_indfile (isfname, rdonly); /* * Open .var file. */ var_fd = _open_varfile (isfname, rdonly); /* * Allocate File Control Block. */ fcb = (Fcb *) _ismalloc(sizeof(*fcb)); memset ((char *) fcb, 0, sizeof(*fcb)); fcb->isfname = _isallocstring(isfname); fcb->rdonly = rdonly; fcb->datfd = dat_fd; fcb->indfd = ind_fd; fcb->varfd = var_fd; /* Key descriptor */ fcb->nkeys = 1; fcb->keys = (Keydesc2 *) _ismalloc(sizeof(fcb->keys[0])); memset((char *)fcb->keys, 0, sizeof(fcb->keys[0])); return (fcb); } /* * _isfcb_nfds(fcb) * * Return the number of UNIX fd consumed by the fcb block. */ int _isfcb_nfds(Fcb *fcb) { int count = 0; if (fcb->datfd != -1) count++; if (fcb->indfd != -1) count++; if (fcb->varfd != -1) count++; return (count); } /* * _isfcb_remove(fcb) * * Remove UNIX files associated with an FCB. */ void _isfcb_remove(Fcb *fcb) { if (fcb->datfd) _remove_datfile(fcb->isfname); if (fcb->indfd) _remove_indfile(fcb->isfname); if (fcb->varfd) _remove_varfile(fcb->isfname); } /* * _isfcb_close(fcb) * * Close UNIX files associated with an FCB, deallocate the FCB block. */ void _isfcb_close(Fcb *fcb) { assert (fcb != NULL); assert (fcb->isfname != NULL); (void) close(fcb->datfd); (void) close(fcb->indfd); (void) close(fcb->varfd); _isfreestring(fcb->isfname); free((char *)fcb->keys); free((char *)fcb); } /* * _isfcb_cntlpg_w(fcb) * * Write information from the control block to the disk. * Note that the Control Page transfer bypasses the disk buffer manager. * * Return 0 if write was successful, return -1 if any error. */ int _isfcb_cntlpg_w(Fcb *fcb) { char cntl_page[ISCNTLSIZE]; int dat_fd = fcb->datfd; int i; /* Clear the page. */ memset (cntl_page, 0, sizeof(cntl_page)); /* Set Magic number. */ (void)strcpy(cntl_page + CP_MAGIC_OFF, ISMAGIC); /* Set Block size */ stshort(ISPAGESIZE, cntl_page + CP_BLOCKSIZE_OFF); /* Set NetISAM version stamp. */ (void)strcpy(cntl_page + CP_VERSION_OFF, ISVERSION); /* .rec file size in blocks. */ stlong((long)fcb->datsize, cntl_page + CP_DATSIZE_OFF); /* .ind file size in blocks. */ stlong((long)fcb->indsize, cntl_page + CP_INDSIZE_OFF); /* .var file size in blocks. */ stlong((long)fcb->varsize, cntl_page + CP_VARSIZE_OFF); /* Variable length 0/1 flag. */ stshort((short)fcb->varflag, cntl_page + CP_VARFLAG_OFF); /* Number of records. */ stlong((long)fcb->nrecords, cntl_page + CP_NRECORDS_OFF); /* Minimum and maximum record length. */ stshort((short)fcb->minreclen, cntl_page + CP_MINRECLEN_OFF); stshort((short)fcb->maxreclen, cntl_page + CP_MAXRECLEN_OFF); /* Last record number. */ strecno(fcb->lastrecno, cntl_page + CP_LASTRECNO_OFF); /* Free record number. */ strecno(fcb->freerecno, cntl_page + CP_FREERECNO_OFF); /* Number of keys */ stshort((short)fcb->nkeys, cntl_page + CP_NKEYS_OFF); /* Last key id */ stlong((long)fcb->lastkeyid, cntl_page + CP_LASTKEYID_OFF); /* ind. free list head */ stlong((long)fcb->indfreelist, cntl_page + CP_INDFREELIST_OFF); /* offset of the end of .var file */ stlong((long)fcb->varend, cntl_page + CP_VAREND_OFF); /* Key descriptors. */ for (i = 0; i < fcb->nkeys; i++) { stkey(fcb->keys + i, cntl_page + CP_KEYS_OFF + i * K2_LEN); } /* Increment stamp1 and stamp2 to indicate change in the Control Page. */ fcb->changestamp1++; fcb->changestamp2++; stlong((long)fcb->changestamp1, cntl_page + CP_CHANGESTAMP1_OFF); stlong((long)fcb->changestamp2, cntl_page + CP_CHANGESTAMP2_OFF); /* * Write the buffer to the disk. */ _isseekpg(dat_fd, ISCNTLPGOFF); _iswritepg(dat_fd, cntl_page); _iswritepg(dat_fd, cntl_page + ISPAGESIZE); return (ISOK); } /* * _isfcb_cntlpg_w2(fcb) * * Write information from the control block to the disk. * Write only selected fields of the control block to avoid the overhead * of coding and decoding. * Note that the Control Page transfer bypasses the disk buffer manager. * * Return 0 if write was successful, return -1 if any error. */ int _isfcb_cntlpg_w2(Fcb *fcb) { char cntl_page[CP_VAREND_OFF+CP_VAREND_LEN]; int dat_fd = fcb->datfd; /* * Read the page from disk. */ _isseekpg(dat_fd, ISCNTLPGOFF); (void)read(dat_fd, cntl_page, sizeof(cntl_page)); /* .rec file size in blocks. */ stlong((long)fcb->datsize, cntl_page + CP_DATSIZE_OFF); /* .ind file size in blocks. */ stlong((long)fcb->indsize, cntl_page + CP_INDSIZE_OFF); /* .var file size in blocks. */ stlong((long)fcb->varsize, cntl_page + CP_VARSIZE_OFF); /* Number of records. */ stlong((long)fcb->nrecords, cntl_page + CP_NRECORDS_OFF); /* Last record number. */ strecno(fcb->lastrecno, cntl_page + CP_LASTRECNO_OFF); /* Free record number. */ strecno(fcb->freerecno, cntl_page + CP_FREERECNO_OFF); /* ind. free list head */ stlong((long)fcb->indfreelist, cntl_page + CP_INDFREELIST_OFF); /* end of .var file */ stlong((long)fcb->varend, cntl_page + CP_VAREND_OFF); /* Increment stamp2 to indicate change in the Control Page. */ fcb->changestamp2++; stlong((long)fcb->changestamp2, cntl_page + CP_CHANGESTAMP2_OFF); /* * Write the buffer to the disk. */ _isseekpg(dat_fd, ISCNTLPGOFF); (void)write(dat_fd, cntl_page, sizeof(cntl_page)); return (ISOK); } /* * _isfcb_cntlpg_r(fcb) * * Read information from control page and store it in the FCB. * Note that the Control Page transfer bypasses the disk buffer manager. * * Return 0 if read was successful, return -1 if any error. */ int _isfcb_cntlpg_r(Fcb *fcb) { char cntl_page[ISCNTLSIZE]; int dat_fd = fcb->datfd; int i; /* * Read the page from the disk. */ _isseekpg(dat_fd, ISCNTLPGOFF); _isreadpg(dat_fd, cntl_page); _isreadpg(dat_fd, cntl_page + ISPAGESIZE); /* block size */ fcb->blocksize = ldshort(cntl_page + CP_BLOCKSIZE_OFF); /* .rec file size in blocks. */ fcb->datsize = ldlong(cntl_page + CP_DATSIZE_OFF); /* .ind file size in blocks. */ fcb->indsize = ldlong(cntl_page + CP_INDSIZE_OFF); /* .var file size in blocks. */ fcb->varsize = ldlong(cntl_page + CP_VARSIZE_OFF); /* Variable length 0/1 flag. */ fcb->varflag = (Bool)ldshort(cntl_page + CP_VARFLAG_OFF); /* Number of records. */ fcb->nrecords = ldlong(cntl_page + CP_NRECORDS_OFF); /* Minimum and maximum record length. */ fcb->minreclen = ldunshort(cntl_page + CP_MINRECLEN_OFF); fcb->maxreclen = ldunshort(cntl_page + CP_MAXRECLEN_OFF); /* Last record number. */ fcb->lastrecno = ldrecno(cntl_page + CP_LASTRECNO_OFF); /* Free record number. */ fcb->freerecno = ldrecno(cntl_page + CP_FREERECNO_OFF); /* Last key id */ fcb->lastkeyid = ldlong(cntl_page + CP_LASTKEYID_OFF); /* .ind free list head. */ fcb->indfreelist = ldlong(cntl_page + CP_INDFREELIST_OFF); /* end of .var file */ fcb->varend = ldlong(cntl_page + CP_VAREND_OFF); /* Number of keys */ fcb->nkeys = ldshort(cntl_page + CP_NKEYS_OFF); /* * Read key descriptors. */ fcb->keys = (Keydesc2 *) _isrealloc((char *)fcb->keys, (unsigned) (sizeof(Keydesc2) * fcb->nkeys)); memset((char *)fcb->keys, 0, sizeof(Keydesc2) * fcb->nkeys); for (i = 0; i < fcb->nkeys; i++) { ldkey(fcb->keys + i, cntl_page + CP_KEYS_OFF + i * K2_LEN); } /* Changestamp1 */ fcb->changestamp1 = ldlong(cntl_page + CP_CHANGESTAMP1_OFF); /* Changestamp2 */ fcb->changestamp2 = ldlong(cntl_page + CP_CHANGESTAMP2_OFF); /* * Open .ind file in situations when some other process has created * keys and this process has just learned it now. */ if (fcb->nkeys > 1 || !FCB_NOPRIMARY_KEY(fcb)) { /* if (_open2_indfile(fcb) != ISOK) _isfatal_error("_open2_indfile()"); */ (void)_open2_indfile(fcb); } return (ISOK); } /* * _isfcb_cntlpg_r2(fcb) * * Read information from the control block on the disk. * Read only selected fields of the control block to avoid the overhead * of coding and decoding. * Note that the Control Page transfer bypasses the disk buffer manager. * * Return 0 if write was successful, return -1 if any error. */ int _isfcb_cntlpg_r2(Fcb *fcb) { char cntl_page[CP_VAREND_OFF+CP_VAREND_LEN]; int dat_fd = fcb->datfd; /* * Read the page from disk. */ _isseekpg(dat_fd, ISCNTLPGOFF); (void)read(dat_fd, cntl_page, sizeof(cntl_page)); /* * Check changestamp1. If the stamp has changed, we must read the entire * page and update the FCB. */ if (ldlong(cntl_page + CP_CHANGESTAMP1_OFF) != fcb->changestamp1) { (void)_isfcb_cntlpg_r(fcb); } /* *_isfcb_cntlpg_r2() is called if transaction is rolled back. * We cannot test changestamp2; we must read the info into the FCB * always. */ #if 0 /* * Check changestamp2. If the stamp has not changed, the FCB contains * up-to-date information. */ if (ldlong(cntl_page + CP_CHANGESTAMP2_OFF) == fcb->changestamp2) { return (ISOK); } #endif /* .rec file size in blocks. */ fcb->datsize = ldlong(cntl_page + CP_DATSIZE_OFF); /* .ind file size in blocks. */ fcb->indsize = ldlong(cntl_page + CP_INDSIZE_OFF); /* .var file size in blocks. */ fcb->varsize = ldlong(cntl_page + CP_VARSIZE_OFF); /* Number of records. */ fcb->nrecords = ldlong(cntl_page + CP_NRECORDS_OFF); /* Last record number. */ fcb->lastrecno = ldrecno(cntl_page + CP_LASTRECNO_OFF); /* Free record number. */ fcb->freerecno = ldrecno(cntl_page + CP_FREERECNO_OFF); /* .ind free list head. */ fcb->indfreelist = ldlong(cntl_page + CP_INDFREELIST_OFF); /* end of .var file */ fcb->varend = ldlong(cntl_page + CP_VAREND_OFF); /* Changestamp2 */ fcb->changestamp2 = ldlong(cntl_page + CP_CHANGESTAMP2_OFF); return (ISOK); } /* * _create_datfile(isfname) * * Create .rec file for ISAM file isfname. */ Static int _create_datfile(char *isfname) { int fd; char namebuf[MAXPATHLEN]; snprintf(namebuf, sizeof(namebuf), "%s", isfname); _makedat_isfname(namebuf); fd = open (namebuf, O_CREAT | O_EXCL | O_RDWR, 0666); if (fd > -1) { /* Close on exec */ if(fcntl(fd, F_SETFD, 1) == -1) { close(fd); return(-1); } } return (fd); } /* * _create_indfile(isfname) * * Create .ind file for ISAM file isfname. */ Static int _create_indfile(char *isfname) { int fd; char namebuf[MAXPATHLEN]; snprintf(namebuf, sizeof(namebuf), "%s", isfname); _makeind_isfname(namebuf); fd = open (namebuf, O_CREAT | O_EXCL | O_RDWR, 0666); if (fd > -1) { /* Close on exec */ if(fcntl(fd, F_SETFD, 1) == -1) { close(fd); return(-1); } } return (fd); } /* * _create_varfile(isfname) * * Create .var file for ISAM file isfname. */ Static int _create_varfile(char *isfname) { int fd; char namebuf[MAXPATHLEN]; snprintf(namebuf, sizeof(namebuf), "%s", isfname); _makevar_isfname(namebuf); fd = open (namebuf, O_CREAT | O_EXCL | O_RDWR, 0666); if (fd > -1) { /* Close on exec */ if(fcntl(fd, F_SETFD, 1) == -1) { close(fd); return(-1); } } return (fd); } /* * _remove_datfile(isfname) * * Remove .rec file for ISAM file isfname. */ Static void _remove_datfile(char *isfname) { char namebuf[MAXPATHLEN]; snprintf(namebuf, sizeof(namebuf), "%s", isfname); _makedat_isfname(namebuf); (void) unlink(namebuf); } /* * _remove_indfile(isfname) * * Remove .ind file for ISAM file isfname. */ Static void _remove_indfile(char *isfname) { char namebuf[MAXPATHLEN]; snprintf(namebuf, sizeof(namebuf), "%s", isfname); _makeind_isfname(namebuf); (void) unlink(namebuf); } /* * _remove_varfile(isfname) * * Remove .var file for ISAM file isfname. */ Static void _remove_varfile(char *isfname) { char namebuf[MAXPATHLEN]; snprintf(namebuf, sizeof(namebuf), "%s", isfname); _makevar_isfname(namebuf); (void) unlink(namebuf); } /* * _open_datfile(isfname) * * Open .rec file for ISAM file isfname. */ Static int _open_datfile(char *isfname, Bool *rdonly) { char namebuf[MAXPATHLEN]; int ret; snprintf(namebuf, sizeof(namebuf), "%s", isfname); _makedat_isfname(namebuf); if ((ret = open (namebuf, O_RDWR)) != -1) { *rdonly = FALSE; /* Close on exec */ if(fcntl(ret, F_SETFD, 1) == -1) { close(ret); ret = -1; } return (ret); } *rdonly = TRUE; ret = open (namebuf, O_RDONLY); if (ret > -1) { /* Close on exec */ if(fcntl(ret, F_SETFD, 1) == -1) { close(ret); return(-1); } } return (ret); } /* * _open_indfile(isfname) * * Open .ind file for ISAM file isfname. */ Static int _open_indfile(char *isfname, Bool rdonly) { int fd; char namebuf[MAXPATHLEN]; snprintf(namebuf, sizeof(namebuf), "%s", isfname); _makeind_isfname(namebuf); fd = open (namebuf, (rdonly==TRUE)?O_RDONLY:O_RDWR); if (fd > -1) { /* Close on exec */ if(fcntl(fd, F_SETFD, 1) == -1) { close(fd); return(-1); } } return (fd); } /* * _open_varfile(isfname) * * Open .var file for ISAM file isfname. */ Static int _open_varfile(char *isfname, Bool rdonly) { int fd; char namebuf[MAXPATHLEN]; snprintf(namebuf, sizeof(namebuf), "%s", isfname); _makevar_isfname(namebuf); fd = open (namebuf, (rdonly==TRUE)?O_RDONLY:O_RDWR); if (fd > -1) { /* Close on exec */ if(fcntl(fd, F_SETFD, 1) == -1) { close(fd); return(-1); } } return (fd); } int _check_isam_magic(Fcb *fcb) { char magicbuffer[CP_MAGIC_LEN]; (void)lseek(fcb->datfd, 0L, 0); if ((read(fcb->datfd, magicbuffer, CP_MAGIC_LEN) < CP_MAGIC_LEN) || /* The following test for compatibilty with `SunISAM 1.0 Beta files. */ ((strncmp(magicbuffer, "SunISAM", strlen(ISMAGIC)) != 0) && (strncmp(magicbuffer, ISMAGIC, strlen(ISMAGIC))) != 0)) { return ISERROR; } else return ISOK; } /* * _open2_indfile(fcb) * * Open (or create) .ind file for ISAM file if the .ind file * is not open already (or does not exist). */ int _open2_indfile(Fcb *fcb) { char namebuf[MAXPATHLEN]; struct stat buf; int openmode; if (fcb->indfd != -1) return (ISOK); snprintf(namebuf, sizeof(namebuf), "%s", fcb->isfname); _makeind_isfname(namebuf); (void)fstat(fcb->datfd, &buf); openmode = (fcb->rdonly) ? O_RDONLY : O_RDWR; if (fcb->indsize == 0) openmode |= O_CREAT; fcb->indfd = open(namebuf, openmode, buf.st_mode); if (fcb->indfd > -1) { /* Close on exec */ if(fcntl(fcb->indfd, F_SETFD, 1) == -1) { close(fcb->indfd); fcb->indfd = -1; } } if(fcb->indfd == -1 && (openmode & O_CREAT)) { _isfatal_error("Cannot create .ind file"); } if (fcb->indfd != -1) { (void) _watchfd_incr(1); (void)fchown(fcb->indfd, buf.st_uid, buf.st_gid); } return ((fcb->indfd == -1) ? ISERROR : ISOK); }