375 lines
12 KiB
C
375 lines
12 KiB
C
/****************************************************************
|
|
* *
|
|
* Copyright 2001, 2012 Fidelity Information Services, Inc *
|
|
* *
|
|
* This source code contains the intellectual property *
|
|
* of its copyright holder(s), and is made available *
|
|
* under a license. If you do not know the terms of *
|
|
* the license, please stop and do not read further. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
#include "mdef.h"
|
|
|
|
#include "gtm_fcntl.h"
|
|
#include "gtm_unistd.h"
|
|
#include "gtm_string.h"
|
|
#include <errno.h>
|
|
#include "gtm_stat.h"
|
|
#include "gtm_stdio.h"
|
|
#include "gtm_statvfs.h"
|
|
|
|
#if defined(__MVS__)
|
|
#include "gtm_zos_io.h"
|
|
#endif
|
|
#include "parse_file.h"
|
|
#include "gdsroot.h"
|
|
#include "gtm_facility.h"
|
|
#include "fileinfo.h"
|
|
#include "gdsbt.h"
|
|
#include "gdsfhead.h"
|
|
#include "gdsblk.h"
|
|
#include "filestruct.h"
|
|
#include "mlkdef.h"
|
|
#include "gtmio.h"
|
|
#include "send_msg.h"
|
|
#include "is_raw_dev.h"
|
|
#include "disk_block_available.h"
|
|
#include "mucregini.h"
|
|
#include "mu_cre_file.h"
|
|
#include "gtmmsg.h"
|
|
#include "util.h"
|
|
#include "gtmdbglvl.h"
|
|
#include "anticipatory_freeze.h"
|
|
#ifdef GTM_CRYPT
|
|
#include "gtmcrypt.h"
|
|
#endif
|
|
#include "shmpool.h" /* Needed for the shmpool structures */
|
|
#include "jnl.h"
|
|
|
|
#define BLK_SIZE (((gd_segment*)gv_cur_region->dyn.addr)->blk_size)
|
|
|
|
#define CLEANUP(XX) \
|
|
{ \
|
|
int rc; \
|
|
\
|
|
if (cc) \
|
|
free(cc); \
|
|
if (cs_data) \
|
|
free(cs_data); \
|
|
if (FD_INVALID != fd) \
|
|
CLOSEFILE_RESET(fd, rc); /* resets "fd" to FD_INVALID */ \
|
|
if (EXIT_ERR == XX) \
|
|
UNLINK(path); \
|
|
}
|
|
|
|
#define SPRINTF_AND_PERROR(MESSAGE) \
|
|
{ \
|
|
save_errno = errno; \
|
|
SPRINTF(errbuff, MESSAGE, path); \
|
|
errno = save_errno; \
|
|
PERROR(errbuff); \
|
|
}
|
|
|
|
#define SPRINTF_AND_PERROR_MVS(MESSAGE) \
|
|
{ \
|
|
save_errno = errno; \
|
|
SPRINTF(errbuff, MESSAGE, path, realfiletag, TAG_BINARY); \
|
|
errno = save_errno; \
|
|
PERROR(errbuff); \
|
|
}
|
|
|
|
GBLREF gd_region *gv_cur_region;
|
|
GBLREF jnlpool_addrs jnlpool;
|
|
GBLREF sgmnt_addrs *cs_addrs;
|
|
GBLREF sgmnt_data_ptr_t cs_data;
|
|
GBLREF uint4 gtmDebugLevel;
|
|
|
|
error_def(ERR_NOSPACECRE);
|
|
error_def(ERR_LOWSPACECRE);
|
|
error_def(ERR_MUNOSTRMBKUP);
|
|
|
|
unsigned char mu_cre_file(void)
|
|
{
|
|
char *cc = NULL, path[MAX_FBUFF + 1], errbuff[512];
|
|
unsigned char buff[DISK_BLOCK_SIZE];
|
|
int fd = FD_INVALID, i, lower, upper, norm_vbn;
|
|
ssize_t status;
|
|
uint4 raw_dev_size; /* size of a raw device, in bytes */
|
|
int4 save_errno;
|
|
gtm_uint64_t avail_blocks, blocks_for_create, blocks_for_extension, delta_blocks;
|
|
file_control fc;
|
|
mstr file;
|
|
parse_blk pblk;
|
|
unix_db_info udi_struct, *udi;
|
|
char *fgets_res;
|
|
gd_segment *seg;
|
|
# ifdef GTM_CRYPT
|
|
char datfile_hash[GTMCRYPT_HASH_LEN];
|
|
int gtmcrypt_errno;
|
|
# endif
|
|
ZOS_ONLY(int realfiletag;)
|
|
|
|
assert((-(SIZEOF(uint4) * 2) & SIZEOF_FILE_HDR_DFLT) == SIZEOF_FILE_HDR_DFLT);
|
|
cs_addrs = &udi_struct.s_addrs;
|
|
cs_data = (sgmnt_data_ptr_t)NULL; /* for CLEANUP */
|
|
memset(&pblk, 0, SIZEOF(pblk));
|
|
pblk.fop = (F_SYNTAXO | F_PARNODE);
|
|
pblk.buffer = path;
|
|
pblk.buff_size = MAX_FBUFF;
|
|
file.addr = (char*)gv_cur_region->dyn.addr->fname;
|
|
file.len = gv_cur_region->dyn.addr->fname_len;
|
|
strncpy(path, file.addr, file.len);
|
|
*(path+file.len) = '\0';
|
|
if (is_raw_dev(path))
|
|
{ /* do not use a default extension for raw device files */
|
|
pblk.def1_buf = DEF_NODBEXT;
|
|
pblk.def1_size = SIZEOF(DEF_NODBEXT) - 1;
|
|
} else
|
|
{
|
|
pblk.def1_buf = DEF_DBEXT;
|
|
pblk.def1_size = SIZEOF(DEF_DBEXT) - 1;
|
|
}
|
|
if (1 != (parse_file(&file, &pblk) & 1))
|
|
{
|
|
PRINTF("Error translating filename %s.\n", file.addr);
|
|
return EXIT_ERR;
|
|
}
|
|
path[pblk.b_esl] = 0;
|
|
if (pblk.fnb & F_HAS_NODE)
|
|
{ /* Remote node specification given */
|
|
assert(pblk.b_node);
|
|
PRINTF("Database file for region %s not created; cannot create across network.\n", path);
|
|
return EXIT_WRN;
|
|
}
|
|
udi = &udi_struct;
|
|
memset(udi, 0, SIZEOF(unix_db_info));
|
|
udi->raw = is_raw_dev(pblk.l_dir);
|
|
# ifdef GTM_CRYPT
|
|
/* Check if this file is an encrypted database. If yes, do init */
|
|
if (gv_cur_region->dyn.addr->is_encrypted)
|
|
{
|
|
INIT_PROC_ENCRYPTION(cs_addrs, gtmcrypt_errno);
|
|
if (0 != gtmcrypt_errno)
|
|
{
|
|
GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, file.len, file.addr);
|
|
return EXIT_ERR;
|
|
}
|
|
}
|
|
# endif
|
|
if (udi->raw)
|
|
{
|
|
fd = OPEN(pblk.l_dir,O_EXCL | O_RDWR);
|
|
if (FD_INVALID == fd)
|
|
{
|
|
SPRINTF_AND_PERROR("Error opening file %s\n");
|
|
return EXIT_ERR;
|
|
}
|
|
if (-1 != (status = (ssize_t)lseek(fd, 0, SEEK_SET)))
|
|
{
|
|
DOREADRC(fd, buff, SIZEOF(buff), status);
|
|
} else
|
|
status = errno;
|
|
if (0 != status)
|
|
{
|
|
SPRINTF_AND_PERROR("Error reading header for file %s\n");
|
|
return EXIT_ERR;
|
|
}
|
|
# ifdef __MVS__
|
|
if (-1 == gtm_zos_tag_to_policy(fd, TAG_BINARY, &realfiletag))
|
|
SPRINTF_AND_PERROR_MVS("Error setting tag policy for file %s (%d) to %d\n");
|
|
# endif
|
|
if (!memcmp(buff, GDS_LABEL, STR_LIT_LEN(GDS_LABEL)))
|
|
{
|
|
char rsp[80];
|
|
PRINTF("Database already exists on device %s\n", path);
|
|
PRINTF("Do you wish to re-initialize (all current data will be lost) [y/n] ? ");
|
|
FGETS(rsp, 79, stdin, fgets_res);
|
|
if ('y' != *rsp)
|
|
return EXIT_NRM;
|
|
}
|
|
PRINTF("Determining size of raw device...\n");
|
|
for(i = 1; read(fd, buff, SIZEOF(buff)) == SIZEOF(buff);)
|
|
{
|
|
i *= 2;
|
|
lseek(fd, (off_t)i * BUFSIZ, SEEK_SET);
|
|
}
|
|
lower = i / 2;
|
|
upper = i;
|
|
while ((lower + upper) / 2 != lower)
|
|
{
|
|
i = (lower + upper) / 2;
|
|
lseek(fd, (off_t)i * BUFSIZ, SEEK_SET);
|
|
if (read(fd, buff, SIZEOF(buff)) == SIZEOF(buff))
|
|
lower = i;
|
|
else
|
|
upper = i;
|
|
}
|
|
raw_dev_size = i * BUFSIZ;
|
|
} else
|
|
{
|
|
fd = OPEN3(pblk.l_dir, O_CREAT | O_EXCL | O_RDWR, 0600);
|
|
if (FD_INVALID == fd)
|
|
{
|
|
SPRINTF_AND_PERROR("Error opening file %s\n");
|
|
return EXIT_ERR;
|
|
}
|
|
# ifdef __MVS__
|
|
if (-1 == gtm_zos_set_tag(fd, TAG_BINARY, TAG_NOTTEXT, TAG_FORCE, &realfiletag))
|
|
SPRINTF_AND_PERROR_MVS("Error setting tag policy for file %s (%d) to %d\n");
|
|
# endif
|
|
if (0 != (save_errno = disk_block_available(fd, &avail_blocks, FALSE)))
|
|
{
|
|
errno = save_errno;
|
|
SPRINTF_AND_PERROR("Error checking available disk space for %s\n");
|
|
CLEANUP(EXIT_ERR);
|
|
return EXIT_ERR;
|
|
}
|
|
seg = gv_cur_region->dyn.addr;
|
|
|
|
/* blocks_for_create is in the unit of DISK_BLOCK_SIZE */
|
|
blocks_for_create = (gtm_uint64_t)(DIVIDE_ROUND_UP(SIZEOF_FILE_HDR_DFLT, DISK_BLOCK_SIZE) + 1 +
|
|
(seg->blk_size / DISK_BLOCK_SIZE *
|
|
(gtm_uint64_t)((DIVIDE_ROUND_UP(seg->allocation, BLKS_PER_LMAP - 1)) + seg->allocation)));
|
|
blocks_for_extension = (seg->blk_size / DISK_BLOCK_SIZE *
|
|
((DIVIDE_ROUND_UP(EXTEND_WARNING_FACTOR * (gtm_uint64_t)seg->ext_blk_count,
|
|
BLKS_PER_LMAP - 1))
|
|
+ EXTEND_WARNING_FACTOR * (gtm_uint64_t)seg->ext_blk_count));
|
|
if (!(gtmDebugLevel & GDL_IgnoreAvailSpace))
|
|
{ /* Bypass this space check if debug flag above is on. Allows us to create a large sparce DB
|
|
* in space it could never fit it if wasn't sparse. Needed for some tests.
|
|
* Also, if the anticipatory freeze scheme is in effect at this point, we would have issued
|
|
* a NOSPACECRE warning (see NOSPACEEXT message which goes through a similar transformation).
|
|
* But at this point, we are guaranteed to not have access to the journal pool or csa both
|
|
* of which are necessary for the ANTICIPATORY_FREEZE_ENABLED(csa) macro so we dont bother
|
|
* to do the warning transformation in this case.
|
|
*/
|
|
assert(NULL == jnlpool.jnlpool_ctl);
|
|
if (avail_blocks < blocks_for_create)
|
|
{
|
|
gtm_putmsg(VARLSTCNT(6) ERR_NOSPACECRE, 4, LEN_AND_STR(path), &blocks_for_create,
|
|
&avail_blocks);
|
|
send_msg(VARLSTCNT(6) ERR_NOSPACECRE, 4, LEN_AND_STR(path), &blocks_for_create,
|
|
&avail_blocks);
|
|
CLEANUP(EXIT_ERR);
|
|
return EXIT_ERR;
|
|
}
|
|
delta_blocks = avail_blocks - blocks_for_create;
|
|
if (delta_blocks < blocks_for_extension)
|
|
{
|
|
gtm_putmsg(VARLSTCNT(8) ERR_LOWSPACECRE, 6, LEN_AND_STR(path), EXTEND_WARNING_FACTOR,
|
|
&blocks_for_extension, DISK_BLOCK_SIZE, &delta_blocks);
|
|
send_msg(VARLSTCNT(8) ERR_LOWSPACECRE, 6, LEN_AND_STR(path), EXTEND_WARNING_FACTOR,
|
|
&blocks_for_extension, DISK_BLOCK_SIZE, &delta_blocks);
|
|
}
|
|
}
|
|
}
|
|
gv_cur_region->dyn.addr->file_cntl = &fc;
|
|
memset(&fc, 0, SIZEOF(file_control));
|
|
fc.file_info = (void*)&udi_struct;
|
|
udi->fd = fd;
|
|
cs_data = (sgmnt_data_ptr_t)malloc(SIZEOF_FILE_HDR_DFLT);
|
|
memset(cs_data, 0, SIZEOF_FILE_HDR_DFLT);
|
|
cs_data->createinprogress = TRUE;
|
|
cs_data->semid = INVALID_SEMID;
|
|
cs_data->shmid = INVALID_SHMID;
|
|
/* We want our datablocks to start on what would be a block boundary within the file which will aid I/O
|
|
* so pad the fileheader if necessary to make this happen.
|
|
*/
|
|
norm_vbn = DIVIDE_ROUND_UP(SIZEOF_FILE_HDR_DFLT, DISK_BLOCK_SIZE) + 1;
|
|
assert(START_VBN_CURRENT >= norm_vbn);
|
|
cs_data->start_vbn = START_VBN_CURRENT;
|
|
cs_data->free_space += (START_VBN_CURRENT - norm_vbn) * DISK_BLOCK_SIZE;
|
|
cs_data->acc_meth = gv_cur_region->dyn.addr->acc_meth;
|
|
if ((dba_mm == cs_data->acc_meth) && (gv_cur_region->jnl_before_image))
|
|
{
|
|
PRINTF("MM access method not compatible with BEFORE image journaling; Database file %s not created.\n", path);
|
|
CLEANUP(EXIT_ERR);
|
|
return EXIT_ERR;
|
|
}
|
|
if (udi->raw)
|
|
{
|
|
/* calculate total blocks, reduce to make room for the
|
|
* database header (size rounded up to a block), then
|
|
* make into a multiple of BLKS_PER_LMAP to have a complete bitmap
|
|
* for each set of blocks.
|
|
*/
|
|
cs_data->trans_hist.total_blks = raw_dev_size - (uint4)ROUND_UP(SIZEOF_FILE_HDR_DFLT, DISK_BLOCK_SIZE);
|
|
cs_data->trans_hist.total_blks /= (uint4)(((gd_segment *)gv_cur_region->dyn.addr)->blk_size);
|
|
if (0 == (cs_data->trans_hist.total_blks - DIVIDE_ROUND_UP(cs_data->trans_hist.total_blks, BLKS_PER_LMAP - 1)
|
|
% (BLKS_PER_LMAP - 1)))
|
|
cs_data->trans_hist.total_blks -= 1; /* don't create a bitmap with no data blocks */
|
|
cs_data->extension_size = 0;
|
|
PRINTF("Raw device size is %dK, %d GDS blocks\n",
|
|
raw_dev_size / 1000,
|
|
cs_data->trans_hist.total_blks);
|
|
} else
|
|
{
|
|
cs_data->trans_hist.total_blks = gv_cur_region->dyn.addr->allocation;
|
|
/* There are (bplmap - 1) non-bitmap blocks per bitmap, so add (bplmap - 2) to number of non-bitmap blocks
|
|
* and divide by (bplmap - 1) to get total number of bitmaps for expanded database. (must round up in this
|
|
* manner as every non-bitmap block must have an associated bitmap)
|
|
*/
|
|
cs_data->trans_hist.total_blks += DIVIDE_ROUND_UP(cs_data->trans_hist.total_blks, BLKS_PER_LMAP - 1);
|
|
cs_data->extension_size = gv_cur_region->dyn.addr->ext_blk_count;
|
|
}
|
|
# ifdef GTM_CRYPT
|
|
/* Check if this file is an encrypted database. If yes, do init */
|
|
if (gv_cur_region->dyn.addr->is_encrypted)
|
|
{
|
|
GTMCRYPT_HASH_GEN(cs_addrs, path, STRLEN(path), datfile_hash, gtmcrypt_errno);
|
|
if (0 != gtmcrypt_errno)
|
|
{
|
|
GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, file.len, file.addr);
|
|
CLEANUP(EXIT_ERR);
|
|
return EXIT_ERR;
|
|
}
|
|
memcpy(cs_data->encryption_hash, datfile_hash, GTMCRYPT_HASH_LEN);
|
|
cs_data->is_encrypted = TRUE; /* Mark this file as encrypted */
|
|
ALLOC_BUFF_GET_ENCR_KEY(cs_addrs, cs_data->encryption_hash, BLK_SIZE, gtmcrypt_errno);
|
|
if (0 != gtmcrypt_errno)
|
|
{
|
|
GTMCRYPT_REPORT_ERROR(gtmcrypt_errno, gtm_putmsg, file.len, file.addr);
|
|
CLEANUP(EXIT_ERR);
|
|
return EXIT_ERR;
|
|
}
|
|
} else
|
|
cs_data->is_encrypted = FALSE;
|
|
# endif
|
|
cs_data->span_node_absent = TRUE;
|
|
cs_data->maxkeysz_assured = TRUE;
|
|
mucregini(cs_data->trans_hist.total_blks);
|
|
cs_data->createinprogress = FALSE;
|
|
DB_LSEEKWRITE(cs_addrs, udi->fn, udi->fd, 0, cs_data, SIZEOF_FILE_HDR_DFLT, status);
|
|
if (0 != status)
|
|
{
|
|
SPRINTF_AND_PERROR("Error writing out header for file %s\n");
|
|
CLEANUP(EXIT_ERR);
|
|
return EXIT_ERR;
|
|
}
|
|
cc = (char*)malloc(DISK_BLOCK_SIZE);
|
|
memset(cc, 0, DISK_BLOCK_SIZE);
|
|
DB_LSEEKWRITE(cs_addrs, udi->fn, udi->fd,
|
|
(cs_data->start_vbn - 1) * DISK_BLOCK_SIZE + ((off_t)(cs_data->trans_hist.total_blks) * cs_data->blk_size),
|
|
cc,
|
|
DISK_BLOCK_SIZE,
|
|
status);
|
|
if (0 != status)
|
|
{
|
|
SPRINTF_AND_PERROR("Error writing out end of file %s\n");
|
|
CLEANUP(EXIT_ERR);
|
|
return EXIT_ERR;
|
|
}
|
|
if ((!udi->raw) && (-1 == CHMOD(pblk.l_dir, 0666)))
|
|
{
|
|
SPRINTF_AND_PERROR("Error changing file mode on file %s\n");
|
|
CLEANUP(EXIT_WRN);
|
|
return EXIT_WRN;
|
|
}
|
|
if ((32 * 1024 - SIZEOF(shmpool_blk_hdr)) < cs_data->blk_size)
|
|
gtm_putmsg(VARLSTCNT(5) ERR_MUNOSTRMBKUP, 3, RTS_ERROR_STRING(path), 32 * 1024 - DISK_BLOCK_SIZE);
|
|
util_out_print("Created file !AD", TRUE, RTS_ERROR_STRING(path));
|
|
CLEANUP(EXIT_NRM);
|
|
return EXIT_NRM;
|
|
}
|