/**************************************************************** * * * Copyright 2001, 2011 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 #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" #ifdef GTM_CRYPT #include "gtmcrypt.h" #endif #include "shmpool.h" /* Needed for the shmpool structures */ #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 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 blocks_for_create, blocks_for_extension, save_errno; GTM_BAVAIL_TYPE avail_blocks; file_control fc; mstr file; parse_blk pblk; unix_db_info udi_struct, *udi; char *fgets_res; gd_segment *seg; GTMCRYPT_ONLY( char datfile_hash[GTMCRYPT_HASH_LEN]; int init_status; int crypt_status; ) 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", gv_cur_region->dyn.addr->fname); 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(init_status); if (0 != init_status) { GC_GTM_PUTMSG(init_status, NULL); 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 = (int4)(DIVIDE_ROUND_UP(SIZEOF_FILE_HDR_DFLT, DISK_BLOCK_SIZE) + 1 + (seg->blk_size / DISK_BLOCK_SIZE * ((DIVIDE_ROUND_UP(seg->allocation, BLKS_PER_LMAP - 1)) + seg->allocation))); 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. */ if ((uint4)avail_blocks < blocks_for_create) { gtm_putmsg(VARLSTCNT(6) ERR_NOSPACECRE, 4, LEN_AND_STR(path), blocks_for_create, (uint4)avail_blocks); send_msg(VARLSTCNT(6) ERR_NOSPACECRE, 4, LEN_AND_STR(path), blocks_for_create, (uint4)avail_blocks); CLEANUP(EXIT_ERR); return EXIT_ERR; } } blocks_for_extension = (seg->blk_size / DISK_BLOCK_SIZE * ((DIVIDE_ROUND_UP(EXTEND_WARNING_FACTOR * seg->ext_blk_count, BLKS_PER_LMAP - 1)) + EXTEND_WARNING_FACTOR * seg->ext_blk_count)); if ((uint4)(avail_blocks - blocks_for_create) < blocks_for_extension) { gtm_putmsg(VARLSTCNT(8) ERR_LOWSPACECRE, 6, LEN_AND_STR(path), EXTEND_WARNING_FACTOR, blocks_for_extension, DISK_BLOCK_SIZE, (uint4)(avail_blocks - blocks_for_create)); send_msg(VARLSTCNT(8) ERR_LOWSPACECRE, 6, LEN_AND_STR(path), EXTEND_WARNING_FACTOR, blocks_for_extension, DISK_BLOCK_SIZE, (uint4)(avail_blocks - blocks_for_create)); } } 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(path, STRLEN(path), datfile_hash, crypt_status); if (0 != crypt_status) { GC_GTM_PUTMSG(crypt_status, gv_cur_region->dyn.addr->fname); 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, crypt_status); if (0 != crypt_status) { GC_GTM_PUTMSG(crypt_status, (gv_cur_region->dyn.addr->fname)); CLEANUP(EXIT_ERR); return EXIT_ERR; } } else cs_data->is_encrypted = FALSE; # endif mucregini(cs_data->trans_hist.total_blks); cs_data->createinprogress = FALSE; LSEEKWRITE(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); LSEEKWRITE(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; }