File file_io.c¶
File List > cubrid > src > storage > file_io.c
Go to the documentation of this file
/*
* Copyright 2008 Search Solution Corporation
* Copyright 2016 CUBRID Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/*
* file_io.c - input/output module (at server)
*/
#ident "$Id$"
#include "config.h"
#include <atomic>
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <limits.h>
#include <sys/stat.h>
#include <assert.h>
#include <signal.h>
#include <dirent.h>
#if defined(WINDOWS)
#include <io.h>
#include <share.h>
#else /* WINDOWS */
#include <unistd.h>
#include <sys/time.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/vfs.h>
#if defined (SERVER_MODE)
#include <syslog.h>
#endif
#endif /* WINDOWS */
#ifdef _AIX
#include <sys/statfs.h>
#endif /* _AIX */
#if defined(SOLARIS)
#include <sys/statvfs.h>
#include <netdb.h>
#endif /* SOLARIS */
#if defined(HPUX)
#include <sys/scsi.h>
#include <aio.h>
#endif /* HPUX */
#include "porting.h"
#include "chartype.h"
#include "connection_globals.h"
#include "file_io.h"
#include "compressor.hpp"
#include "storage_common.h"
#include "memory_alloc.h"
#include "error_manager.h"
#include "system_parameter.h"
#include "message_catalog.h"
#include "msgcat_set_log.hpp"
#include "object_representation.h"
#include "util_func.h"
#include "perf_monitor.h"
#include "environment_variable.h"
#include "connection_error.h"
#include "release_string.h"
#include "log_common_impl.h"
#include "log_volids.hpp"
#include "fault_injection.h"
#include "es_posix.h"
#if defined (SERVER_MODE)
#include "vacuum.h"
#endif /* SERVER_MODE */
#include "crypt_opfunc.h"
#if defined(WINDOWS)
#include "wintcp.h"
#else /* WINDOWS */
#include "tcp.h"
#endif /* WINDOWS */
#if defined(SERVER_MODE)
#include "connection_error.h"
#include "network_interface_sr.h"
#endif /* SERVER_MODE */
#if !defined (CS_MODE)
#include "double_write_buffer.hpp"
#include "page_buffer.h"
#include "xserver_interface.h"
#endif /* !defined (CS_MODE) */
#include "intl_support.h"
#include "tsc_timer.h"
#if defined (SERVER_MODE)
#include "thread_worker_pool_impl.hpp" // for system_core_count
#include "server_support.h"
#endif // SERVER_MODE
#if defined (SERVER_MODE)
#include "thread_entry_task.hpp"
#endif // SERVER_MODE
#if defined (SERVER_MODE) || defined (SA_MODE)
#include "thread_manager.hpp" // for thread_get_thread_entry_info and thread_sleep
#endif // SERVER_MODE
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"
/************************************************************************/
/* TODO: why is this in client module? */
/************************************************************************/
/* Suppress GCC -Wformat-truncation warnings for PATH_MAX-based snprintf usage */
#if defined (__GNUC__)
#define DISABLE_FMT_TRUNC_WARNING \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wformat-truncation\"")
#define ENABLE_FMT_TRUNC_WARNING \
_Pragma("GCC diagnostic pop")
#else
#define DISABLE_FMT_TRUNC_WARNING
#define ENABLE_FMT_TRUNC_WARNING
#endif
/*
* Message id in the set MSGCAT_SET_IO
* in the message catalog MSGCAT_CATALOG_CUBRID (file cubrid.msg).
*/
#define MSGCAT_FILEIO_STARTS 1
#define MSGCAT_FILEIO_BKUP_NEEDED 2
#define MSGCAT_FILEIO_BKUP_HDR 3
#define MSGCAT_FILEIO_BKUP_HDR_MAGICID 4
#define MSGCAT_FILEIO_BKUP_HDR_RELEASES 5
#define MSGCAT_FILEIO_BKUP_HDR_DBINFO 6
#define MSGCAT_FILEIO_BKUP_HDR_LEVEL 7
#define MSGCAT_FILEIO_BKUP_HDR_TIME 8
#define MSGCAT_FILEIO_BKUP_FILE 9
#define MSGCAT_FILEIO_REST_RELO_NEEDED 10
#define MSGCAT_FILEIO_REST_RELO_OPTIONS 11
#define MSGCAT_FILEIO_NEWLOCATION 12
#define MSGCAT_FILEIO_INPUT_RANGE_ERROR 13
#define MSGCAT_FILEIO_INCORRECT_BKVOLUME 14
#define MSGCAT_FILEIO_LEVEL_MISMATCH 15
#define MSGCAT_FILEIO_MAGIC_MISMATCH 16
#define MSGCAT_FILEIO_DB_MISMATCH 17
#define MSGCAT_FILEIO_UNIT_NUM_MISMATCH 18
#define MSGCAT_FILEIO_BACKUP_TIME_MISMATCH 19
#define MSGCAT_FILEIO_BACKUP_VINF_ERROR 20
#define MSGCAT_FILEIO_BACKUP_LABEL_INFO 21
#define MSGCAT_FILEIO_BKUP_HDR_LX_LSA 22
#define MSGCAT_FILEIO_RESTORE_FIND_REASON 23
#define MSGCAT_FILEIO_BKUP_FIND_REASON 24
#define MSGCAT_FILEIO_BKUP_PREV_BKVOL 25
#define MSGCAT_FILEIO_BKUP_NEXT_BKVOL 26
#define MSGCAT_FILEIO_BKUP_HDR_BKUP_PAGESIZE 27
#define MSGCAT_FILEIO_BKUP_HDR_ZIP_INFO 28
#define MSGCAT_FILEIO_BKUP_HDR_INC_ACTIVELOG 29
#ifdef L_cuserid
#define FILEIO_USER_NAME_SIZE L_cuserid
#else /* L_cuserid */
#define FILEIO_USER_NAME_SIZE 9
#endif /* L_cuserid */
#if defined(WINDOWS)
#define GETPID() GetCurrentProcessId()
#else /* WINDOWS */
#define GETPID() getpid()
#endif /* WINDOWS */
#define FILEIO_DISK_FORMAT_MODE (O_RDWR | O_CREAT)
#define FILEIO_DISK_PROTECTION_MODE 0600
#define FILEIO_MAX_WAIT_DBTXT 300
#define FILEIO_FULL_LEVEL_EXP 32
/*
* Define a fixed size for backup and restore input/output of the volume
* headers. For most modern devices multiples of 512 or 1024 are needed.
* A size of the header data is computed in compile.
*/
#define GET_NEXT_1K_SIZE(s) (((((s) - 1) / 1024) + 1) * 1024)
#define FILEIO_BACKUP_HEADER_IO_SIZE GET_NEXT_1K_SIZE(sizeof(FILEIO_BACKUP_HEADER))
#define FILEIO_GET_FILE_SIZE(pagesize, npages) \
(((off_t)(pagesize)) * ((off_t)(npages)))
#define FILEIO_BACKUP_NO_ZIP_HEADER_VERSION 1
#define FILEIO_BACKUP_CURRENT_HEADER_VERSION 2
#define FILEIO_CHECK_FOR_INTERRUPT_INTERVAL 100
#define FILEIO_PAGE_SIZE_FULL_LEVEL (IO_PAGESIZE * FILEIO_FULL_LEVEL_EXP)
#define FILEIO_BACKUP_PAGE_OVERHEAD \
(offsetof(FILEIO_BACKUP_PAGE, iopage) + sizeof(PAGEID))
#define FILEIO_BACKUP_DBVOLS_IO_PAGE_SIZE \
(IO_PAGESIZE + FILEIO_BACKUP_PAGE_OVERHEAD)
#define FILEIO_BACKUP_DBVOLS_IO_PAGE_SIZE_FULL_LEVEL \
(FILEIO_PAGE_SIZE_FULL_LEVEL + FILEIO_BACKUP_PAGE_OVERHEAD)
#define FILEIO_RESTORE_DBVOLS_IO_PAGE_SIZE(sess) \
((sess)->bkup.bkuphdr->bkpagesize + FILEIO_BACKUP_PAGE_OVERHEAD)
#define FILEIO_DBVOLS_IO_PAGE_SIZE(backup_header_p) \
((backup_header_p)->bkpagesize + FILEIO_BACKUP_PAGE_OVERHEAD)
#define FILEIO_BACKUP_FILE_HEADER_PAGE_SIZE \
(sizeof(FILEIO_BACKUP_FILE_HEADER) + offsetof(FILEIO_BACKUP_PAGE, iopage))
/* Set just the redundant copy of the pageid to the given page. */
#define FILEIO_SET_BACKUP_PAGE_ID_COPY(area, pageid, psize) \
*(PAGEID *)(((char *)(area)) + \
(offsetof(FILEIO_BACKUP_PAGE, iopage) + psize)) = pageid
/* Set the backup page pageid(s) */
#define FILEIO_SET_BACKUP_PAGE_ID(area, pageid, psize) \
do { \
((FILEIO_BACKUP_PAGE *)(area))->iopageid = pageid; \
/* set the redundant copy of the pageid, alignment is important */ \
FILEIO_SET_BACKUP_PAGE_ID_COPY(area, pageid, psize); \
} while (false);
/* Get the backup page primary pageid */
#define FILEIO_GET_BACKUP_PAGE_ID(area) (((FILEIO_BACKUP_PAGE *)(area))->iopageid)
/*
* Verify the integrity of the page just read by checking the redundant
* copy of the pageid. Remember that the ->iopageid_copy cannot be accessed
* directly and must be retrieved by pointer offset. If the two pageid's
* do not match it is probably a corrupted page.
*/
#define FILEIO_CHECK_RESTORE_PAGE_ID(area, pagesz) \
(((FILEIO_BACKUP_PAGE *)(area))->iopageid == \
*(PAGEID *)(((char *)(area)) + offsetof(FILEIO_BACKUP_PAGE, iopage) + pagesz))
/* Define minimum number of pages required for a backup volume
For now, specify at least 4 pages plus the header. */
#define FILEIO_BACKUP_MINIMUM_NUM_PAGES \
CEIL_PTVDIV((FILEIO_BACKUP_HEADER_IO_SIZE + \
(FILEIO_BACKUP_DBVOLS_IO_PAGE_SIZE) * 4), IO_PAGESIZE)
#define FILEIO_BACKUP_MINIMUM_NUM_PAGES_FULL_LEVEL \
CEIL_PTVDIV((FILEIO_BACKUP_HEADER_IO_SIZE + \
(FILEIO_BACKUP_DBVOLS_IO_PAGE_SIZE_FULL_LEVEL) * 4), IO_PAGESIZE)
#define FILEIO_CHECK_AND_INITIALIZE_VOLUME_HEADER_CACHE(rtn) \
do { \
if (fileio_Vol_info_header.volinfo == NULL \
&& fileio_initialize_volume_info_cache () < 0) \
return (rtn); \
} while (0)
/* Some specifications of page identifiers of backup */
#define FILEIO_BACKUP_START_PAGE_ID (-2)
#define FILEIO_BACKUP_END_PAGE_ID (-3)
#define FILEIO_BACKUP_FILE_START_PAGE_ID (-4)
#define FILEIO_BACKUP_FILE_END_PAGE_ID (-5)
#define FILEIO_BACKUP_VOL_CONT_PAGE_ID (-6)
#define FILEIO_END_OF_FILE (1)
/* Minimum flush rate 40MB/s */
#define FILEIO_MIN_FLUSH_PAGES_PER_SEC (41943040 / IO_PAGESIZE)
/* TODO: Growth/drop flush rate values can be tweaked. They have been set to
* meet the needs of stressful workload. They have been set to drop slowly and
* grow back quickly.
* Please consider that token consumption depend on two factors:
* 1. System IO capabilities.
* 2. Flush thinking time.
* If flush thinking time prevents it from consuming tokens, we might reduce
* the numbers of token for next iterations unwillingly. A fast drop rate and
* slow growth rate can make it impossible to recover from a "missed"
* iteration (e.g. flush has been blocked on AIN list mutex).
*/
/* Rate of growing flush rate when tokens are consumed. */
#define FILEIO_PAGE_FLUSH_GROW_RATE 0.5
/* Rate of reducing flush rate when tokens are not consumed. */
#define FILEIO_PAGE_FLUSH_DROP_RATE 0.1
#if defined(WINDOWS)
#define fileio_lock_file_write(fd, offset, whence, len) \
fileio_lock_region(fd, F_TLOCK, offset, len)
#define fileio_lock_file_writew(fd, offset, whence, len) \
fileio_lock_region(fd, F_LOCK, offset, len)
#define fileio_lock_file_read(fd, offset, whence, len) \
fileio_lock_region(fd, F_TLOCK, offset, len)
#define fileio_lock_file_readw(fd, offset, whence, len) \
fileio_lock_region(fd, F_LOCK, offset, len)
#define fileio_unlock_file(fd, offset, whence, len) \
fileio_lock_region(fd, F_ULOCK, offset, len)
#else /* WINDOWS */
#define fileio_lock_file_read(fd, offset, whence, len) \
fileio_lock_region(fd, F_SETLK, F_RDLCK, offset, whence, len)
#define fileio_lock_file_readw(fd, offset, whence, len) \
fileio_lock_region(fd, F_SETLKW, F_RDLCK, offset, whence, len)
#define fileio_lock_file_write(fd, offset, whence, len) \
fileio_lock_region(fd, F_SETLK, F_WRLCK, offset, whence, len)
#define fileio_lock_file_writew(fd, offset, whence, len) \
fileio_lock_region(fd, F_SETLKW, F_WRLCK, offset, whence, len)
#define fileio_unlock_file(fd, offset, whence, len) \
fileio_lock_region(fd, F_SETLK, F_UNLCK, offset, whence, len)
#endif /* WINDOWS */
#define FILEIO_VOLINFO_INCREMENT 32
#if !defined(SERVER_MODE)
#define pthread_mutex_init(a, b)
#define pthread_mutex_destroy(a)
#define pthread_mutex_lock(a) 0
#define pthread_mutex_unlock(a)
#endif
/* User input states when requesting relocation of a (backup) volume. */
typedef enum
{
FILEIO_RELOCATION_FIRST = 0,
FILEIO_RELOCATION_QUIT = FILEIO_RELOCATION_FIRST,
FILEIO_RELOCATION_RETRY,
FILEIO_RELOCATION_ALTERNATE,
FILEIO_RELOCATION_LAST = FILEIO_RELOCATION_ALTERNATE
} FILEIO_RELOCATION_VOLUME;
typedef struct fileio_backup_file_header FILEIO_BACKUP_FILE_HEADER;
typedef struct fileio_bkvinf_entry FILEIO_BACKUP_INFO_ENTRY;
typedef struct fileio_bkvinf_queues FILEIO_BACKUP_INFO_QUEUE;
typedef struct fileio_sys_volinfo FILEIO_SYSTEM_VOLUME_INFO;
typedef struct fileio_sys_volinfo_header FILEIO_SYSTEM_VOLUME_HEADER;
typedef struct fileio_volinfo FILEIO_VOLUME_INFO;
typedef struct fileio_volinfo_header FILEIO_VOLUME_HEADER;
/* A FILE/VOLUME HEADER IN BACKUP */
struct fileio_backup_file_header
{
INT64 nbytes;
VOLID volid;
short dummy1; /* Dummy field for 8byte align */
int dummy2; /* Dummy field for 8byte align */
char vlabel[PATH_MAX];
};
/* Some specifications for bkvinf data */
/* Each one of these represents a given backup volume unit */
struct fileio_bkvinf_entry
{
int unit_num;
char bkvol_name[PATH_MAX];
FILEIO_BACKUP_INFO_ENTRY *link;
};
/* Master data structure to retain information about each backup level */
struct fileio_bkvinf_queues
{
bool initialized;
FILEIO_BACKUP_INFO_ENTRY *anchors[FILEIO_BACKUP_UNDEFINED_LEVEL];
FILEIO_BACKUP_INFO_ENTRY *free;
};
/* Volume information structure for system volumes(volid < NULL_VOLID) */
struct fileio_sys_volinfo
{
VOLID volid;
int vdes;
FILEIO_LOCKF_TYPE lockf_type;
char vlabel[PATH_MAX];
#if defined(SERVER_MODE) && defined(WINDOWS)
pthread_mutex_t sysvol_mutex;
#endif /* SERVER_MODE && WINDOWS */
FILEIO_SYSTEM_VOLUME_INFO *next;
};
/* System volume informations are linked as a list */
struct fileio_sys_volinfo_header
{
#if defined(SERVER_MODE)
pthread_mutex_t mutex;
#endif /* SERVER_MODE */
int num_vols;
FILEIO_SYSTEM_VOLUME_INFO anchor;
};
/* Volume information structure for perm/temp volumes */
struct fileio_volinfo
{
VOLID volid;
int vdes;
FILEIO_LOCKF_TYPE lockf_type;
#if defined(SERVER_MODE) && defined(WINDOWS)
pthread_mutex_t vol_mutex; /* for fileio_read()/fileio_write() */
#endif /* SERVER_MODE && WINDOWS */
char vlabel[PATH_MAX];
};
typedef union fileio_apply_function_arg
{
int vol_id;
int vdes;
const char *vol_label;
} APPLY_ARG;
/* Perm/temp volume informations are stored on array.
* Direct access by volid is possible */
struct fileio_volinfo_header
{
#if defined(SERVER_MODE)
pthread_mutex_t mutex;
#endif /* SERVER_MODE */
int max_perm_vols; /* # of max. io_volinfo entries for perm. vol */
int next_perm_volid; /* # of used io_volinfo entries for perm. vol */
int max_temp_vols; /* # of max. io_volinfo entries for temp. vol */
int next_temp_volid; /* # of used io_volinfo entries for temp. vol */
/* if volid of volume is equal to this value, */
/* it is temp. volume */
int num_volinfo_array; /* # of io_volinfo entry chunks */
FILEIO_VOLUME_INFO **volinfo; /* array of pointer for io_volinfo chunks */
};
typedef bool (*VOLINFO_APPLY_FN) (THREAD_ENTRY * thread_p, FILEIO_VOLUME_INFO * vol_info_p, APPLY_ARG * arg);
typedef bool (*SYS_VOLINFO_APPLY_FN) (THREAD_ENTRY * thread_p, FILEIO_SYSTEM_VOLUME_INFO * sys_vol_info_p,
APPLY_ARG * arg);
static FILEIO_SYSTEM_VOLUME_HEADER fileio_Sys_vol_info_header = {
#if defined(SERVER_MODE)
PTHREAD_MUTEX_INITIALIZER,
#endif /* SERVER_MODE */
0,
{
NULL_VOLID, NULL_VOLDES, FILEIO_NOT_LOCKF, "",
#if defined(SERVER_MODE) && defined(WINDOWS)
PTHREAD_MUTEX_INITIALIZER,
#endif /* SERVER_MODE && WINDOWS */
NULL}
};
static FILEIO_VOLUME_HEADER fileio_Vol_info_header = {
#if defined(SERVER_MODE)
PTHREAD_MUTEX_INITIALIZER,
#endif /* SERVER_MODE */
0, 0, 0, LOG_MAX_DBVOLID, 0, NULL
};
/* Records information from the bkvinf file about backup volumes */
static FILEIO_BACKUP_INFO_QUEUE fileio_Backup_vol_info_data[2] =
{ {false, {NULL, NULL, NULL}, NULL}, {false, {NULL, NULL, NULL}, NULL} };
/* Flush Control */
#if !defined(HAVE_ATOMIC_BUILTINS)
static pthread_mutex_t fileio_Flushed_page_counter_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
static int fileio_Flushed_page_count = 0;
static TOKEN_BUCKET fc_Token_bucket_s;
static TOKEN_BUCKET *fc_Token_bucket = NULL;
static FLUSH_STATS fc_Stats;
#if defined(CUBRID_DEBUG)
/* Set this to get various levels of io information regarding
* backup and restore activity.
* 0 :: no output
* 1 :: print names and sizes of volumes that are backed-up.
* 2 :: dump page bitmaps after volume is restored.
*/
static int io_Bkuptrace_debug = -1;
#endif /* CUBRID_DEBUG */
#if defined(SERVER_MODE) && defined(WINDOWS)
static pthread_mutex_t *fileio_get_volume_mutex (THREAD_ENTRY * thread_p, int vdes);
#endif
static int fileio_initialize_volume_info_cache (void);
static void fileio_make_volume_lock_name (char *vol_lockname, const char *vol_fullname);
static int fileio_create (THREAD_ENTRY * thread_p, const char *db_fullname, const char *vlabel, VOLID volid,
bool dolock, bool dosync);
static int fileio_create_backup_volume (THREAD_ENTRY * thread_p, const char *db_fullname, const char *vlabel,
VOLID volid, bool dolock, bool dosync, int atleast_pages);
static int fileio_max_permanent_volumes (int index, int num_permanent_volums);
static int fileio_min_temporary_volumes (int index, int num_temp_volums, int num_volinfo_array);
static FILEIO_SYSTEM_VOLUME_INFO *fileio_traverse_system_volume (THREAD_ENTRY * thread_p,
SYS_VOLINFO_APPLY_FN apply_function, APPLY_ARG * arg);
static FILEIO_VOLUME_INFO *fileio_traverse_permanent_volume (THREAD_ENTRY * thread_p, VOLINFO_APPLY_FN apply_function,
APPLY_ARG * arg);
static FILEIO_VOLUME_INFO *fileio_reverse_traverse_permanent_volume (THREAD_ENTRY * thread_p,
VOLINFO_APPLY_FN apply_function, APPLY_ARG * arg);
static FILEIO_VOLUME_INFO *fileio_traverse_temporary_volume (THREAD_ENTRY * thread_p, VOLINFO_APPLY_FN apply_function,
APPLY_ARG * arg);
static FILEIO_VOLUME_INFO *fileio_reverse_traverse_temporary_volume (THREAD_ENTRY * thread_p,
VOLINFO_APPLY_FN apply_function, APPLY_ARG * arg);
static bool fileio_dismount_volume (THREAD_ENTRY * thread_p, FILEIO_VOLUME_INFO * vol_info_p, APPLY_ARG * ignore_arg);
static bool fileio_is_volume_descriptor_equal (THREAD_ENTRY * thread_p, FILEIO_VOLUME_INFO * vol_info_p,
APPLY_ARG * arg);
static bool fileio_is_volume_id_gt (THREAD_ENTRY * thread_p, FILEIO_VOLUME_INFO * vol_info_p, APPLY_ARG * arg);
static bool fileio_is_volume_id_lt (THREAD_ENTRY * thread_p, FILEIO_VOLUME_INFO * vol_info_p, APPLY_ARG * arg);
static FILEIO_SYSTEM_VOLUME_INFO *fileio_find_system_volume (THREAD_ENTRY * thread_p,
SYS_VOLINFO_APPLY_FN apply_function, APPLY_ARG * arg);
static bool fileio_is_system_volume_descriptor_equal (THREAD_ENTRY * thread_p,
FILEIO_SYSTEM_VOLUME_INFO * sys_vol_info_p, APPLY_ARG * arg);
static bool fileio_is_system_volume_id_equal (THREAD_ENTRY * thread_p, FILEIO_SYSTEM_VOLUME_INFO * sys_vol_info_p,
APPLY_ARG * arg);
static bool fileio_is_system_volume_label_equal (THREAD_ENTRY * thread_p, FILEIO_SYSTEM_VOLUME_INFO * sys_vol_info_p,
APPLY_ARG * arg);
static bool fileio_synchronize_sys_volume (THREAD_ENTRY * thread_p, FILEIO_SYSTEM_VOLUME_INFO * vol_sys_info_p,
APPLY_ARG * arg);
static bool fileio_synchronize_volume (THREAD_ENTRY * thread_p, FILEIO_VOLUME_INFO * vol_info_p, APPLY_ARG * arg);
static int fileio_cache (VOLID volid, const char *vlabel, int vdes, FILEIO_LOCKF_TYPE lockf_type);
static void fileio_decache (THREAD_ENTRY * thread_p, int vdes);
static VOLID fileio_get_volume_id (int vdes);
static bool fileio_is_volume_label_equal (THREAD_ENTRY * thread_p, FILEIO_VOLUME_INFO * vol_info_p, APPLY_ARG * arg);
static int fileio_expand_permanent_volume_info (FILEIO_VOLUME_HEADER * header, int volid);
static int fileio_expand_temporary_volume_info (FILEIO_VOLUME_HEADER * header, int volid);
static bool fileio_is_terminated_process (int pid);
static ssize_t fileio_os_read (THREAD_ENTRY * thread_p, int vol_fd, void *io_page_p, size_t count, off_t offset);
static ssize_t fileio_os_write (THREAD_ENTRY * thread_p, int vol_fd, void *io_page_p, size_t count, off_t offset);
#if !defined (WINDOWS)
static ssize_t pwrite_with_injected_fault (THREAD_ENTRY * thread_p, int fd, const void *buf, size_t count,
off_t offset);
#endif
#if !defined(WINDOWS)
static FILEIO_LOCKF_TYPE fileio_lock (const char *db_fullname, const char *vlabel, int vdes, bool dowait);
static void fileio_unlock (const char *vlabel, int vdes, FILEIO_LOCKF_TYPE lockf_type);
static FILEIO_LOCKF_TYPE fileio_get_lockf_type (int vdes);
#endif /* !WINDOWS */
static int fileio_get_primitive_way_max (const char *path, long int *filename_max, long int *pathname_max);
static int fileio_flush_backup (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session);
static ssize_t fileio_read_backup (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session, int pageid);
static int fileio_write_backup (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session, ssize_t towrite_nbytes);
static int fileio_write_backup_header (FILEIO_BACKUP_SESSION * session);
static FILEIO_BACKUP_SESSION *fileio_initialize_restore (THREAD_ENTRY * thread_p, const char *db_fullname,
char *backup_src, FILEIO_BACKUP_SESSION * session,
FILEIO_BACKUP_LEVEL level,
const char *restore_verbose_file_path, bool newvolpath);
static int fileio_read_restore (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session, int toread_nbytes);
static void *fileio_write_restore (THREAD_ENTRY * thread_p, FILEIO_RESTORE_PAGE_BITMAP * page_bitmap, int vdes,
void *io_pgptr, VOLID vol_id, PAGEID page_id, FILEIO_BACKUP_LEVEL level);
static int fileio_read_restore_header (FILEIO_BACKUP_SESSION * session);
static FILEIO_RELOCATION_VOLUME fileio_find_restore_volume (THREAD_ENTRY * thread_p, const char *dbname,
char *to_volname, int unit_num, FILEIO_BACKUP_LEVEL level,
int reason);
static int fileio_get_next_backup_volume (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session, bool user_new);
static int fileio_initialize_backup_info (int which_bkvinf);
static FILEIO_BACKUP_INFO_ENTRY *fileio_allocate_backup_info (int which_bkvinf);
static FILEIO_BACKUP_SESSION *fileio_continue_restore (THREAD_ENTRY * thread_p, const char *db_fullname,
INT64 db_creation, FILEIO_BACKUP_SESSION * session,
bool first_time, bool authenticate, INT64 match_bkupcreation);
static int fileio_fill_hole_during_restore (THREAD_ENTRY * thread_p, int *next_pageid, int stop_pageid,
FILEIO_BACKUP_SESSION * session, FILEIO_RESTORE_PAGE_BITMAP * page_bitmap);
static int fileio_decompress_restore_volume (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session, int nbytes);
static FILEIO_NODE *fileio_allocate_node (FILEIO_QUEUE * qp, FILEIO_BACKUP_HEADER * backup_hdr);
static FILEIO_NODE *fileio_free_node (FILEIO_QUEUE * qp, FILEIO_NODE * node);
static FILEIO_NODE *fileio_delete_queue_head (FILEIO_QUEUE * qp);
static int fileio_compress_backup_node (FILEIO_NODE * node, FILEIO_BACKUP_HEADER * backup_hdr);
static int fileio_write_backup_node (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session, FILEIO_NODE * node,
FILEIO_BACKUP_HEADER * backup_hdr);
static char *fileio_ctime (INT64 * clock, char *buf);
static const char *fileio_get_backup_level_string (FILEIO_BACKUP_LEVEL level);
static int fileio_initialize_backup_thread (FILEIO_BACKUP_SESSION * session_p, int num_threads);
static void fileio_finalize_backup_thread (FILEIO_BACKUP_SESSION * session_p, FILEIO_ZIP_METHOD zip_method);
static int fileio_write_backup_end_time_to_header (FILEIO_BACKUP_SESSION * session_p, INT64 end_time);
static void fileio_write_backup_end_time_to_last_page (FILEIO_BACKUP_SESSION * session_p, INT64 end_time);
static void fileio_read_backup_end_time_from_last_page (FILEIO_BACKUP_SESSION * session_p);
#if !defined(WINDOWS)
static int fileio_get_lock (int fd, const char *vlabel);
static int fileio_release_lock (int fd);
static int fileio_lock_region (int fd, int cmd, int type, off_t offset, int whence, off_t len);
#endif /* !WINDOWS */
#if defined(SERVER_MODE)
static void fileio_read_backup_volume (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session);
static FILEIO_TYPE fileio_write_backup_volume (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session);
static FILEIO_NODE *fileio_append_queue (FILEIO_QUEUE * qp, FILEIO_NODE * node);
#endif /* SERVER_MODE */
static void fileio_compensate_flush (THREAD_ENTRY * thread_p, int fd, int npage);
static int fileio_increase_flushed_page_count (int npages);
static int fileio_flush_control_get_token (THREAD_ENTRY * thread_p, int ntoken);
static int fileio_flush_control_get_desired_rate (TOKEN_BUCKET * tb);
static int fileio_synchronize_bg_archive_volume (THREAD_ENTRY * thread_p);
static void fileio_page_bitmap_set (FILEIO_RESTORE_PAGE_BITMAP * page_bitmap, int page_id);
static bool fileio_page_bitmap_is_set (FILEIO_RESTORE_PAGE_BITMAP * page_bitmap, int page_id);
static void fileio_page_bitmap_dump (FILE * out_fp, const FILEIO_RESTORE_PAGE_BITMAP * page_bitmap);
static int
fileio_increase_flushed_page_count (int npages)
{
int flushed_page_count;
#if defined(HAVE_ATOMIC_BUILTINS)
flushed_page_count = ATOMIC_INC_32 (&fileio_Flushed_page_count, npages);
#else
(void) pthread_mutex_lock (&fileio_Flushed_page_counter_mutex);
fileio_Flushed_page_count += npages;
flushed_page_count = fileio_Flushed_page_count;
pthread_mutex_unlock (&fileio_Flushed_page_counter_mutex);
#endif /* HAVE_ATOMIC_BUILTINS */
return flushed_page_count;
}
static void
fileio_compensate_flush (THREAD_ENTRY * thread_p, int fd, int npage)
{
#if !defined(SERVER_MODE)
return;
#else
int rv;
bool need_sync;
int flushed_page_count;
assert (npage > 0);
if (npage <= 0)
{
return;
}
if (thread_p == NULL)
{
thread_p = thread_get_thread_entry_info ();
}
rv = fileio_flush_control_get_token (thread_p, npage);
if (rv != NO_ERROR)
{
return;
}
need_sync = false;
flushed_page_count = fileio_increase_flushed_page_count (npage);
if (flushed_page_count > prm_get_integer_value (PRM_ID_PB_SYNC_ON_NFLUSH))
{
need_sync = true;
fileio_Flushed_page_count = 0;
}
if (need_sync)
{
fileio_synchronize_all (thread_p);
}
#endif /* SERVER_MODE */
}
/*
* fileio_flush_control_initialize():
*
* returns:
*
* Note:
*/
int
fileio_flush_control_initialize (void)
{
#if !defined(SERVER_MODE)
return NO_ERROR;
#else
TOKEN_BUCKET *tb;
int rv = NO_ERROR;
assert (fc_Token_bucket == NULL);
tb = &fc_Token_bucket_s;
rv = pthread_mutex_init (&tb->token_mutex, NULL);
if (rv != NO_ERROR)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CSS_PTHREAD_MUTEX_INIT, 0);
return ER_CSS_PTHREAD_MUTEX_INIT;
}
tb->tokens = 0;
tb->token_consumed = 0;
rv = pthread_cond_init (&tb->waiter_cond, NULL);
if (rv != 0)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CSS_PTHREAD_COND_INIT, 0);
return ER_CSS_PTHREAD_COND_INIT;
}
fc_Stats.num_tokens = 0;
fc_Stats.num_log_pages = 0;
fc_Stats.num_pages = 0;
fc_Token_bucket = tb;
return rv;
#endif
}
/*
* fileio_flush_control_finalize():
*
* returns:
*
* Note:
*/
void
fileio_flush_control_finalize (void)
{
#if !defined(SERVER_MODE)
return;
#else
TOKEN_BUCKET *tb;
assert (fc_Token_bucket != NULL);
if (fc_Token_bucket == NULL)
{
return;
}
tb = fc_Token_bucket;
fc_Token_bucket = NULL;
(void) pthread_mutex_destroy (&tb->token_mutex);
(void) pthread_cond_destroy (&tb->waiter_cond);
#endif
}
/*
* fileio_flush_control_get_token():
*
* returns:
*
* Note:
*/
static int
fileio_flush_control_get_token (THREAD_ENTRY * thread_p, int ntoken)
{
#if !defined(SERVER_MODE)
return NO_ERROR;
#else
TOKEN_BUCKET *tb = fc_Token_bucket;
int rv = NO_ERROR;
int retry_count = 0;
int nreq;
bool log_cs_own = false;
PERF_UTIME_TRACKER time_tracker = PERF_UTIME_TRACKER_INITIALIZER;
if (tb == NULL)
{
return NO_ERROR;
}
assert (ntoken > 0);
if (LOG_CS_OWN (thread_p))
{
log_cs_own = true;
}
nreq = ntoken;
while (nreq > 0 && retry_count < 10)
{
/* try to get a token from share tokens */
rv = pthread_mutex_lock (&tb->token_mutex);
assert (rv == NO_ERROR);
if (log_cs_own == true)
{
fc_Stats.num_log_pages += nreq;
}
else
{
fc_Stats.num_pages += nreq;
}
if (tb->tokens >= nreq)
{
tb->tokens -= nreq;
tb->token_consumed += nreq;
pthread_mutex_unlock (&tb->token_mutex);
return NO_ERROR;
}
else if (tb->tokens > 0)
{
nreq -= tb->tokens;
tb->token_consumed += tb->tokens;
tb->tokens = 0;
}
assert (nreq > 0);
if (log_cs_own == true)
{
pthread_mutex_unlock (&tb->token_mutex);
return NO_ERROR;
}
PERF_UTIME_TRACKER_START (thread_p, &time_tracker);
/* Wait for signal */
rv = pthread_cond_wait (&tb->waiter_cond, &tb->token_mutex);
pthread_mutex_unlock (&tb->token_mutex);
retry_count++;
PERF_UTIME_TRACKER_BULK_TIME (thread_p, &time_tracker, PSTAT_PB_COMPENSATE_FLUSH, nreq);
}
/* I am very very unlucky (unlikely to happen) */
er_log_debug (ARG_FILE_LINE, "Failed to get token within %d trial (req=%d, remained=%d)", retry_count, ntoken, nreq);
return NO_ERROR;
#endif
}
/*
* fileio_flush_control_add_tokens():
*
* returns:
*
* Note:
*/
int
fileio_flush_control_add_tokens (THREAD_ENTRY * thread_p, INT64 diff_usec, int *token_gen, int *token_consumed)
{
#if !defined(SERVER_MODE)
return NO_ERROR;
#else
TOKEN_BUCKET *tb = fc_Token_bucket;
int gen_tokens;
int rv = NO_ERROR;
assert (token_gen != NULL);
if (tb == NULL)
{
return NO_ERROR;
}
/* add remaining tokens to shared tokens */
rv = pthread_mutex_lock (&tb->token_mutex);
*token_consumed = tb->token_consumed;
tb->token_consumed = 0;
perfmon_add_stat (thread_p, PSTAT_FC_NUM_PAGES, fc_Stats.num_pages);
perfmon_add_stat (thread_p, PSTAT_FC_NUM_LOG_PAGES, fc_Stats.num_log_pages);
perfmon_add_stat (thread_p, PSTAT_FC_TOKENS, fc_Stats.num_tokens);
if (prm_get_bool_value (PRM_ID_ADAPTIVE_FLUSH_CONTROL) == true)
{
/* Get desired rate from evaluating changes in last iteration. */
gen_tokens = fileio_flush_control_get_desired_rate (tb);
/* Check new rate is not below minimum required. */
gen_tokens = (int) MAX (gen_tokens, (double) FILEIO_MIN_FLUSH_PAGES_PER_SEC * (double) diff_usec / 1000000.0);
}
else
{
/* Always set maximum rate. */
gen_tokens = (int) (prm_get_integer_value (PRM_ID_MAX_FLUSH_PAGES_PER_SECOND) * (double) diff_usec / 1000000.0);
}
*token_gen = gen_tokens;
/* initialization statistics */
fc_Stats.num_pages = 0;
fc_Stats.num_log_pages = 0;
fc_Stats.num_tokens = gen_tokens;
tb->tokens = gen_tokens;
/* signal to waiters */
pthread_cond_broadcast (&tb->waiter_cond);
pthread_mutex_unlock (&tb->token_mutex);
return rv;
#endif
}
/*
* fileio_flush_control_get_desired_rate () -
*
*/
static int
fileio_flush_control_get_desired_rate (TOKEN_BUCKET * tb)
{
#if !defined (SERVER_MODE)
return 0;
#else
int dirty_rate = pgbuf_flush_control_from_dirty_ratio ();
int adjust_rate = fc_Stats.num_tokens; /* Start with previous rate. */
if (tb->tokens > 0)
{
if (dirty_rate > 0)
{
/* This is difficult situation. We did not consume all tokens but dirty rate goes up. Let's keep the number
* of tokens until dirty rate is no longer an issue. */
}
else
{
/* Do not drop the tokens too fast. If for any reason flush has been completely stopped, tokens drop to
* minimum directly. */
adjust_rate -= (int) (tb->tokens * FILEIO_PAGE_FLUSH_DROP_RATE);
}
}
else
{
/* We need to increase the rate. */
adjust_rate += MAX (dirty_rate, (int) (fc_Stats.num_tokens * FILEIO_PAGE_FLUSH_GROW_RATE));
}
return adjust_rate;
#endif
}
/*
* fileio_initialize_volume_info_cache () - Allocate/initialize
* volinfo_header.volinfo array
* return: 0 if success, or -1
*
* Note: This function is usually first called by
* fileio_find_volume_descriptor_with_label()(normal startup,
* backup/restore etc.) or fileio_mount()(database creation time)
*/
static int
fileio_initialize_volume_info_cache (void)
{
int i, n;
int rv;
rv = pthread_mutex_lock (&fileio_Vol_info_header.mutex);
if (fileio_Vol_info_header.volinfo == NULL)
{
n = (VOLID_MAX - 1) / FILEIO_VOLINFO_INCREMENT + 1;
fileio_Vol_info_header.volinfo = (FILEIO_VOLUME_INFO **) malloc (sizeof (FILEIO_VOLUME_INFO *) * n);
if (fileio_Vol_info_header.volinfo == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (FILEIO_VOLUME_INFO *) * n);
pthread_mutex_unlock (&fileio_Vol_info_header.mutex);
return -1;
}
fileio_Vol_info_header.num_volinfo_array = n;
for (i = 0; i < fileio_Vol_info_header.num_volinfo_array; i++)
{
fileio_Vol_info_header.volinfo[i] = NULL;
}
}
pthread_mutex_unlock (&fileio_Vol_info_header.mutex);
return 0;
}
static int
fileio_allocate_and_initialize_volume_info (FILEIO_VOLUME_HEADER * header_p, int idx)
{
FILEIO_VOLUME_INFO *vol_info_p;
int i;
header_p->volinfo[idx] = NULL;
vol_info_p = (FILEIO_VOLUME_INFO *) malloc (sizeof (FILEIO_VOLUME_INFO) * FILEIO_VOLINFO_INCREMENT);
if (vol_info_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
sizeof (FILEIO_VOLUME_INFO) * FILEIO_VOLINFO_INCREMENT);
return ER_FAILED;
}
for (i = 0; i < FILEIO_VOLINFO_INCREMENT; i++)
{
vol_info_p[i].volid = NULL_VOLID;
vol_info_p[i].vdes = NULL_VOLDES;
vol_info_p[i].lockf_type = FILEIO_NOT_LOCKF;
vol_info_p[i].vlabel[0] = '\0';
#if defined(WINDOWS)
pthread_mutex_init (&vol_info_p[i].vol_mutex, NULL);
#endif /* WINDOWS */
}
header_p->volinfo[idx] = vol_info_p;
return NO_ERROR;
}
/*
* fileio_expand_permanent_volume_info () - Expand io_volinfo chunks to cache
* volid volume information
* return: 0 if success, or -1
* header(in):
* volid(in):
*
* Note: Permanent volume informations are stored from volid 0 to
* header->max_perm_vols. If header->max_perm_vols is less than volid,
* allocate new io_volinfo chunk.
*/
static int
fileio_expand_permanent_volume_info (FILEIO_VOLUME_HEADER * header_p, int volid)
{
int from_idx, to_idx;
int rv;
rv = pthread_mutex_lock (&header_p->mutex);
from_idx = (header_p->max_perm_vols / FILEIO_VOLINFO_INCREMENT);
to_idx = (volid + 1) / FILEIO_VOLINFO_INCREMENT;
/* check if to_idx chunks are used for temp volume information */
if (to_idx >= (header_p->num_volinfo_array - 1 - header_p->max_temp_vols / FILEIO_VOLINFO_INCREMENT))
{
pthread_mutex_unlock (&header_p->mutex);
return -1;
}
for (; from_idx <= to_idx; from_idx++)
{
if (fileio_allocate_and_initialize_volume_info (header_p, from_idx) != NO_ERROR)
{
pthread_mutex_unlock (&header_p->mutex);
return -1;
}
header_p->max_perm_vols = (from_idx + 1) * FILEIO_VOLINFO_INCREMENT;
}
pthread_mutex_unlock (&header_p->mutex);
return 0;
}
/*
* fileio_expand_temporary_volume_info () - Expand io_volinfo chunks to cache
* volid volume information
* return: 0 if success, or -1
* header(in):
* volid(in):
*
* Note: Temporary volume informations are stored from volid LOG_MAX_DBVOLID to
* LOG_MAX_DBVOLID-header->max_temp_vols.
* If LOG_MAX_DBVOLID-header->max_temp_vols is greater than volid,
* allocate new io_volinfo chunk.
*/
static int
fileio_expand_temporary_volume_info (FILEIO_VOLUME_HEADER * header_p, int volid)
{
int from_idx, to_idx;
int rv;
rv = pthread_mutex_lock (&header_p->mutex);
from_idx = header_p->num_volinfo_array - 1 - (header_p->max_temp_vols / FILEIO_VOLINFO_INCREMENT);
to_idx = header_p->num_volinfo_array - 1 - ((LOG_MAX_DBVOLID - volid) / FILEIO_VOLINFO_INCREMENT);
/* check if to_idx chunks are used for perm. volume information */
if (to_idx <= (header_p->max_perm_vols - 1) / FILEIO_VOLINFO_INCREMENT)
{
pthread_mutex_unlock (&header_p->mutex);
return -1;
}
for (; from_idx >= to_idx; from_idx--)
{
if (fileio_allocate_and_initialize_volume_info (header_p, from_idx) != NO_ERROR)
{
pthread_mutex_unlock (&header_p->mutex);
return -1;
}
header_p->max_temp_vols = (header_p->num_volinfo_array - from_idx) * FILEIO_VOLINFO_INCREMENT;
}
pthread_mutex_unlock (&header_p->mutex);
return 0;
}
/* TODO: recoding to use APR
*
* fileio_ctime() - VARIANT OF NORMAL CTIME THAT ALWAYS REMOVES THE NEWLINE
* return: ptr to time string returned by ctime
* time_t(in):
* buf(in):
*
* Note: Strips the \n off the end of the string returned by ctime.
* this routine is really general purpose, there may be other users
* of ctime.
*/
static char *
fileio_ctime (INT64 * clock_p, char *buffer_p)
{
char *p, *t;
time_t tmp_time;
tmp_time = (time_t) (*clock_p);
t = ctime_r (&tmp_time, buffer_p);
p = strchr (t, '\n');
if (p)
{
*p = '\0';
}
return (t);
}
/*
* fileio_is_terminated_process () -
* return:
* pid(in):
*/
static bool
fileio_is_terminated_process (int pid)
{
#if defined(WINDOWS)
HANDLE h_process;
h_process = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, pid);
if (h_process == NULL)
{
return true;
}
else
{
CloseHandle (h_process);
return false;
}
#else /* WINDOWS */
if (kill (pid, 0) == -1)
{
return true;
}
else
{
return false;
}
#endif /* WINDOWS */
}
#if !defined(WINDOWS)
/*
* fileio_lock () - LOCKF A DATABASE VOLUME
* return:
* db_fullname(in): Name of the database where the volume belongs
* vlabel(in): Volume label
* vdes(in): Volume descriptor
* dowait(in): true when it is ok to wait for the lock (databases.txt)
*
*/
static FILEIO_LOCKF_TYPE
fileio_lock (const char *db_full_name_p, const char *vol_label_p, int vol_fd, bool dowait)
{
FILE *fp;
char name_info_lock[PATH_MAX];
char host[CUB_MAXHOSTNAMELEN];
char host2[CUB_MAXHOSTNAMELEN];
char user[FILEIO_USER_NAME_SIZE];
char login_name[FILEIO_USER_NAME_SIZE];
INT64 lock_time;
long long tmp_lock_time;
int pid;
bool retry = true;
int lockf_errno;
FILEIO_LOCKF_TYPE result = FILEIO_LOCKF;
int total_num_loops = 0;
int num_loops = 0;
int max_num_loops;
char io_timeval[CTIME_MAX], format_string[32];
if (prm_get_bool_value (PRM_ID_IO_LOCKF_ENABLE) != true)
{
return FILEIO_LOCKF;
}
#if defined(CUBRID_DEBUG)
struct stat stbuf;
/*
* Make sure that advisory locks are used. An advisory lock is desired
* since we are observing a voluntarily locking scheme.
* Mandatory locks are know to be dangerous. If a runaway or otherwise
* out-of-control process should hold a mandatory lock on the database
* and fail to release that lock, the entire database system could hang
*/
if (fstat (vol_fd, &stbuf) != -1)
{
if ((stbuf.st_mode & S_ISGID) != 0 && (stbuf.st_mode & S_IRWXG) != S_IXGRP)
{
er_log_debug (ARG_FILE_LINE, "A mandatory lock will be set on file = %s", vol_label_p);
}
}
#endif /* CUBRID_DEBUG */
if (vol_label_p == NULL)
{
vol_label_p = "";
}
max_num_loops = FILEIO_MAX_WAIT_DBTXT;
fileio_make_volume_lock_name (name_info_lock, vol_label_p);
/*
* NOTE: The lockby auxiliary file is created only after we have acquired
* the lock. This is important to avoid a possible synchronization
* problem with this secundary technique
*/
sprintf (format_string, "%%%ds %%d %%%ds %%lld", FILEIO_USER_NAME_SIZE - 1, CUB_MAXHOSTNAMELEN - 1);
again:
while (retry == true && fileio_lock_file_write (vol_fd, 0, SEEK_SET, 0) < 0)
{
if (errno == EINTR)
{
/* Retry if the an interruption was signed */
retry = true;
continue;
}
lockf_errno = errno;
retry = false;
/* Volume seems to be mounted by someone else. Find out who has it. */
fp = fopen (name_info_lock, "r");
if (fp == NULL)
{
(void) sleep (3);
num_loops += 3;
total_num_loops += 3;
fp = fopen (name_info_lock, "r");
if (fp == NULL && num_loops <= 3)
{
/*
* Note that we try to check for the lock only one more time,
* unless we have been waiting for a while
* (Case of dowait == false,
* note that num_loops is set to 0 when waiting for a lock).
*/
retry = true;
continue;
}
}
if (fp == NULL || fscanf (fp, format_string, user, &pid, host, &tmp_lock_time) != 4)
{
strcpy (user, "???");
strcpy (host, "???");
pid = 0;
lock_time = 0;
}
else
{
lock_time = tmp_lock_time;
}
/* Make sure that the process holding the lock is not a run away process. A run away process is one of the
* following: 1) If the lockby file exist and the following is true: same user, same host, and lockby process
* does not exist any longer */
if (fp == NULL)
{
/* It is no more true that if the lockby file does not exist, then it is the run away process. When the user
* cannot get the file lock, it means that the another process who owns the database exists. */
fileio_ctime (&lock_time, io_timeval);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_LOCKED, 6, vol_label_p, db_full_name_p, user, pid, host,
(lock_time == 0) ? "???" : io_timeval);
return FILEIO_NOT_LOCKF;
}
else
{
(void) fclose (fp);
*host2 = '\0';
cuserid ((char *) login_name);
login_name[FILEIO_USER_NAME_SIZE - 1] = '\0';
if (!
(strcmp (user, login_name) == 0 && GETHOSTNAME (host2, CUB_MAXHOSTNAMELEN) == 0
&& strcmp (host, host2) == 0 && fileio_is_terminated_process (pid) != 0 && errno == ESRCH))
{
if (dowait != false)
{
/*
* NOBODY USES dowait EXPECT DATABASE.TXT
*
* It would be nice if we could use a wait function to wait on a
* process that is not a child process.
* Wait until the process is gone if we are in the same machine,
* otherwise, continue looping.
*/
while (fileio_is_volume_exist (name_info_lock) == true && num_loops < 60
&& total_num_loops < max_num_loops)
{
if (strcmp (host, host2) == 0 && fileio_is_terminated_process (pid) != 0)
{
break;
}
(void) sleep (3);
num_loops += 3;
total_num_loops += 3;
}
if (total_num_loops < max_num_loops)
{
retry = true;
num_loops = 0;
goto again;
}
}
/* not a run away process */
fileio_ctime (&lock_time, io_timeval);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_LOCKED, 6, vol_label_p, db_full_name_p, user, pid,
host, io_timeval);
return FILEIO_NOT_LOCKF;
}
}
#if defined(CUBRID_DEBUG)
er_log_debug (ARG_FILE_LINE,
"io_lock: WARNING ignoring a run away lock on volume = %s\n. lockd deamon may not be"
" working right.\n UNIX error = %s", vol_label_p, strerror (lockf_errno));
#endif /* CUBRID_DEBUG */
}
/* Create the information lock file and write the information about the lock */
fp = fopen (name_info_lock, "w");
if (fp != NULL)
{
if (GETHOSTNAME (host, CUB_MAXHOSTNAMELEN) != 0)
{
strcpy (host, "???");
}
if (getuserid (login_name, FILEIO_USER_NAME_SIZE) == NULL)
{
strcpy (login_name, "???");
}
(void) fprintf (fp, "%s %d %s %ld", login_name, (int) GETPID (), host, time (NULL));
(void) fclose (fp);
}
else
{
/* Unable to create the lockf file. */
if (result == FILEIO_LOCKF)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, name_info_lock);
fileio_unlock (vol_label_p, vol_fd, result);
result = FILEIO_NOT_LOCKF;
}
}
return result;
}
/*
* fileio_lock_la_log_path () - LOCKF A applylogdb logpath lock
* return:
* db_fullname(in): Name of the database where the volume belongs
* lock_path(in): Lock file path
* vdes(in): Volume descriptor
* last_deleted_arv_num(out):
*
*/
FILEIO_LOCKF_TYPE
fileio_lock_la_log_path (const char *db_full_name_p, const char *lock_path_p, int vol_fd, int *last_deleted_arv_num)
{
FILE *fp;
char host[CUB_MAXHOSTNAMELEN];
char user[FILEIO_USER_NAME_SIZE];
char login_name[FILEIO_USER_NAME_SIZE];
INT64 lock_time;
long long tmp_lock_time;
int pid;
bool retry = true;
int lockf_errno;
FILEIO_LOCKF_TYPE result = FILEIO_LOCKF;
int num_loops = 0;
char io_timeval[64], format_string[32];
#if defined(CUBRID_DEBUG)
struct stat stbuf;
/*
* Make sure that advisory locks are used. An advisory lock is desired
* since we are observing a voluntarily locking scheme.
* Mandatory locks are know to be dangerous. If a runaway or otherwise
* out-of-control process should hold a mandatory lock on the database
* and fail to release that lock, the entire database system could hang
*/
if (fstat (vol_fd, &stbuf) != -1)
{
if ((stbuf.st_mode & S_ISGID) != 0 && (stbuf.st_mode & S_IRWXG) != S_IXGRP)
{
er_log_debug (ARG_FILE_LINE, "A mandatory lock will be set on file = %s", vol_label_p);
}
}
#endif /* CUBRID_DEBUG */
if (lock_path_p == NULL)
{
lock_path_p = "";
}
/*
* NOTE: The lockby auxiliary file is created only after we have acquired
* the lock. This is important to avoid a possible synchronization
* problem with this secundary technique
*/
sprintf (format_string, "%%d %%%ds %%d %%%ds %%lld", FILEIO_USER_NAME_SIZE - 1, CUB_MAXHOSTNAMELEN - 1);
while (retry == true && fileio_lock_file_write (vol_fd, 0, SEEK_SET, 0) < 0)
{
if (errno == EINTR)
{
/* Retry if the an interruption was signed */
retry = true;
continue;
}
lockf_errno = errno;
retry = false;
/* Volume seems to be mounted by someone else. Find out who has it. */
fp = fopen (lock_path_p, "r");
if (fp == NULL)
{
(void) sleep (3);
num_loops += 3;
fp = fopen (lock_path_p, "r");
if (fp == NULL && num_loops <= 3)
{
retry = true;
continue;
}
}
if (fp == NULL || fscanf (fp, format_string, last_deleted_arv_num, user, &pid, host, &tmp_lock_time) != 5)
{
strcpy (user, "???");
strcpy (host, "???");
pid = 0;
lock_time = 0;
*last_deleted_arv_num = -1;
}
else
{
lock_time = tmp_lock_time;
}
if (fp != NULL)
{
(void) fclose (fp);
}
#if defined(CUBRID_DEBUG)
er_log_debug (ARG_FILE_LINE,
"io_lock: WARNING ignoring a run away lock on volume = %s\n. lockd deamon may not be"
" working right.\n UNIX error = %s", lock_path_p, strerror (lockf_errno));
#endif /* CUBRID_DEBUG */
memset (io_timeval, 0, sizeof (io_timeval));
fileio_ctime (&lock_time, io_timeval);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_LOCKED, 6, lock_path_p, db_full_name_p, user, pid, host,
(lock_time == 0) ? "???" : io_timeval);
return FILEIO_NOT_LOCKF;
}
/* Create the information lock file and write the information about the lock */
fp = fdopen (vol_fd, "w+");
if (fp != NULL)
{
if (fscanf (fp, format_string, last_deleted_arv_num, user, &pid, host, &tmp_lock_time) != 5)
{
*last_deleted_arv_num = -1;
}
fseek (fp, 0, SEEK_SET);
if (GETHOSTNAME (host, CUB_MAXHOSTNAMELEN) != 0)
{
strcpy (host, "???");
}
if (getuserid (login_name, FILEIO_USER_NAME_SIZE) == NULL)
{
strcpy (login_name, "???");
}
if (*last_deleted_arv_num < 0)
{
*last_deleted_arv_num = -1;
}
(void) fprintf (fp, "%-10d %s %d %s %ld", *last_deleted_arv_num, login_name, (int) GETPID (), host, time (NULL));
fflush (fp);
}
else
{
/* Unable to create the lockf file. */
if (result == FILEIO_LOCKF)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, lock_path_p);
result = FILEIO_NOT_LOCKF;
}
}
return result;
}
/*
* fileio_lock_la_dbname () - LOCKF A applylogdb database lock
* return:
*
* lockf_vdes(in): lock file descriptor
* db_name(in): database name
* log_path(in): log file path
*
*/
FILEIO_LOCKF_TYPE
fileio_lock_la_dbname (int *lockf_vdes, char *db_name, char *log_path)
{
int error = NO_ERROR;
int fd = NULL_VOLDES;
int pid;
int r;
FILEIO_LOCKF_TYPE result = FILEIO_LOCKF;
FILE *fp = NULL;
char lock_dir[PATH_MAX], lock_path[PATH_MAX];
char tmp_db_name[DB_MAX_IDENTIFIER_LENGTH], tmp_log_path[PATH_MAX];
char format_string[PATH_MAX];
envvar_vardir_file (lock_dir, sizeof (lock_dir), "APPLYLOGDB");
if (snprintf (lock_path, sizeof (lock_path) - 1, "%s/%s", lock_dir, db_name) < 0)
{
assert (false);
result = FILEIO_NOT_LOCKF;
goto error_return;
}
if (access (lock_dir, F_OK) < 0)
{
/* create parent directory if not exist */
if (mkdir (lock_dir, 0777) < 0 && errno == ENOENT)
{
char pdir[PATH_MAX];
if (cub_dirname_r (lock_dir, pdir, PATH_MAX) > 0 && access (pdir, F_OK) < 0)
{
mkdir (pdir, 0777);
}
}
}
if (access (lock_dir, F_OK) < 0)
{
if (mkdir (lock_dir, 0777) < 0)
{
er_log_debug (ARG_FILE_LINE, "unable to create dir (%s)", lock_dir);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_BO_DIRECTORY_DOESNOT_EXIST, 1, lock_dir);
result = FILEIO_NOT_LOCKF;
goto error_return;
}
}
snprintf (format_string, sizeof (format_string), "%%d %%%ds %%%ds", DB_MAX_IDENTIFIER_LENGTH - 1, PATH_MAX - 1);
fd = fileio_open (lock_path, O_RDWR | O_CREAT, 0644);
if (fd == NULL_VOLDES)
{
er_log_debug (ARG_FILE_LINE, "unable to open lock_file (%s)", lock_path);
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, lock_path);
result = FILEIO_NOT_LOCKF;
goto error_return;
}
fp = fopen (lock_path, "r");
if (fp)
{
fseek (fp, (off_t) 0, SEEK_SET);
r = fscanf (fp, format_string, &pid, tmp_db_name, tmp_log_path);
if (r == 3)
{
assert_release (strcmp (db_name, tmp_db_name) == 0);
if (strcmp (db_name, tmp_db_name) || strcmp (log_path, tmp_log_path))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_LOCKED, 6, lock_path, db_name, "-", pid, "-", "-");
fclose (fp);
result = FILEIO_NOT_LOCKF;
goto error_return;
}
}
fclose (fp);
}
else
{
er_log_debug (ARG_FILE_LINE, "unable to open lock_file (%s)", lock_path);
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, lock_path);
result = FILEIO_NOT_LOCKF;
goto error_return;
}
if (fileio_lock_file_write (fd, 0, SEEK_SET, 0) < 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_LOCKED, 6, lock_path, db_name, "-", 0, "-", "-");
result = FILEIO_NOT_LOCKF;
goto error_return;
}
fp = fopen (lock_path, "w+");
if (fp)
{
fseek (fp, (off_t) 0, SEEK_SET);
pid = getpid ();
fprintf (fp, "%-10d %s %s", pid, db_name, log_path);
fflush (fp);
fclose (fp);
}
else
{
error = fileio_release_lock (fd);
assert_release (error == NO_ERROR);
er_log_debug (ARG_FILE_LINE, "unable to open lock_file (%s)", lock_path);
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, lock_path);
result = FILEIO_NOT_LOCKF;
goto error_return;
}
(*lockf_vdes) = fd;
return result;
error_return:
if (fd != NULL_VOLDES)
{
fileio_close (fd);
fd = NULL_VOLDES;
}
(*lockf_vdes) = fd;
return result;
}
/*
* fileio_unlock_la_dbname () - UNLOCKF A applylogdb database lock
* return:
*
* lockf_vdes(in): lock file descriptor
* db_name(in): database name
* clear_owner(in): clear lock owner
*
*/
FILEIO_LOCKF_TYPE
fileio_unlock_la_dbname (int *lockf_vdes, char *db_name, bool clear_owner)
{
FILEIO_LOCKF_TYPE result;
int error;
off_t end_offset;
FILE *fp = NULL;
char lock_dir[PATH_MAX], lock_path[PATH_MAX];
envvar_vardir_file (lock_dir, sizeof (lock_dir), "APPLYLOGDB");
if (snprintf (lock_path, sizeof (lock_path) - 1, "%s/%s", lock_dir, db_name) < 0)
{
assert (false);
return FILEIO_NOT_LOCKF;
}
if (access (lock_dir, F_OK) < 0)
{
er_log_debug (ARG_FILE_LINE, "lock directory does not exist (%s)", lock_dir);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_BO_DIRECTORY_DOESNOT_EXIST, 1, lock_dir);
return FILEIO_NOT_LOCKF;
}
assert_release ((*lockf_vdes) != NULL_VOLDES);
if ((*lockf_vdes) == NULL_VOLDES)
{
return FILEIO_NOT_LOCKF;
}
if (clear_owner)
{
fp = fopen (lock_path, "w+");
if (fp == NULL)
{
er_log_debug (ARG_FILE_LINE, "unable to open lock_file (%s)", lock_path);
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, lock_path);
return FILEIO_LOCKF;
}
fseek (fp, (off_t) 0, SEEK_END);
end_offset = ftell (fp);
fseek (fp, (off_t) 0, SEEK_SET);
if (end_offset > 0)
{
fprintf (fp, "%*s", (int) end_offset, " ");
}
fflush (fp);
fclose (fp);
}
error = fileio_release_lock ((*lockf_vdes));
if (error == NO_ERROR)
{
result = FILEIO_NOT_LOCKF;
}
else
{
assert_release (error == NO_ERROR);
result = FILEIO_LOCKF;
}
if (result == FILEIO_NOT_LOCKF)
{
fileio_close ((*lockf_vdes));
(*lockf_vdes) = NULL_VOLDES;
}
return result;
}
static void
fileio_check_lockby_file (char *name_info_lock_p)
{
/*
* Either we did not acuire the lock through flock or seek has failed.
* Use secundary technique for verification.
* Make sure that current process has the lock, that is, check if
* these are the consecuences of a run away process. If the lockby file
* indicates that current process has the lock, remove the lockby file
* to indicate that the process does not have the lock any longer.
*/
FILE *fp;
int pid;
char login_name[FILEIO_USER_NAME_SIZE];
char user[FILEIO_USER_NAME_SIZE];
char host[CUB_MAXHOSTNAMELEN];
char host2[CUB_MAXHOSTNAMELEN];
char format_string[32];
fp = fopen (name_info_lock_p, "r");
if (fp != NULL)
{
sprintf (format_string, "%%%ds %%d %%%ds", FILEIO_USER_NAME_SIZE - 1, CUB_MAXHOSTNAMELEN - 1);
if (fscanf (fp, format_string, user, &pid, host) != 3)
{
strcpy (user, "???");
strcpy (host, "???");
pid = 0;
}
(void) fclose (fp);
/* Check for same process, same user, same host */
getuserid (login_name, FILEIO_USER_NAME_SIZE);
if (pid == GETPID () && strcmp (user, login_name) == 0 && GETHOSTNAME (host2, CUB_MAXHOSTNAMELEN) == 0
&& strcmp (host, host2) == 0)
{
(void) remove (name_info_lock_p);
}
}
}
/*
* fileio_unlock () - UNLOCK A DATABASE VOLUME
* return: void
* vlabel(in): Volume label
* vdes(in): Volume descriptor
* lockf_type(in): Type of lock
*
* Note: The volume associated with the given name is unlocked and the
* lock information file is removed.
* If the Unix system complains that the volume is not locked by
* the requested process, the information lock file is consulted
* to verify for run a way process. If the requested process is
* identical to the one recorded in the information lock, the
* function returns without any error, otherwise, an error is set
* and an error condition is returned.
*/
static void
fileio_unlock (const char *vol_label_p, int vol_fd, FILEIO_LOCKF_TYPE lockf_type)
{
char name_info_lock[PATH_MAX];
if (prm_get_bool_value (PRM_ID_IO_LOCKF_ENABLE) == true)
{
if (vol_label_p == NULL)
{
vol_label_p = "";
}
strcpy (name_info_lock, vol_label_p);
fileio_make_volume_lock_name (name_info_lock, vol_label_p);
/*
* We must remove the lockby file before we call flock to unlock the file.
* Otherwise, we may remove the file when is locked by another process
* Case of preemption and another process acquiring the lock.
*/
if (lockf_type != FILEIO_LOCKF)
{
fileio_check_lockby_file (name_info_lock);
}
else
{
(void) remove (name_info_lock);
fileio_unlock_file (vol_fd, 0, SEEK_SET, 0);
}
}
}
#endif /* !WINDOWS */
/*
* fileio_initialize_pages () - Initialize the first npages of the given volume with the
* content of given page
* return: io_pgptr on success, NULL on failure
* vdes(in): Volume descriptor
* io_pgptr(in): Initialization content of all pages
* npages(in): Number of pages to initialize
* ensure_metadata(in): Include metadata when syncing.
* kbytes_to_be_written_per_sec : size to add volume per sec
*/
void *
fileio_initialize_pages (THREAD_ENTRY * thread_p, int vol_fd, FILEIO_PAGE * io_page_p, DKNPAGES start_pageid,
DKNPAGES npages, size_t page_size, bool ensure_metadata, int kbytes_to_be_written_per_sec)
{
PAGEID page_id;
bool skip_flush = false;
#if defined (SERVER_MODE)
int count_of_page_for_a_sleep = 10;
INT64 allowed_millis_for_a_sleep = 0; /* time which is time for writing unit of page and sleeping in a sleep */
INT64 previous_elapsed_millis; /* time which is previous time for writing unit of page and sleep */
INT64 time_to_sleep;
TSC_TICKS start_tick, end_tick;
TSCTIMEVAL tv_diff;
INT64 page_count_per_sec;
#endif
#if defined (SERVER_MODE)
if (kbytes_to_be_written_per_sec > 0)
{
page_count_per_sec = kbytes_to_be_written_per_sec / (IO_PAGESIZE / ONE_K);
if (page_count_per_sec < count_of_page_for_a_sleep)
{
page_count_per_sec = count_of_page_for_a_sleep;
}
allowed_millis_for_a_sleep = count_of_page_for_a_sleep * 1000 / page_count_per_sec;
tsc_getticks (&start_tick);
}
#endif
#if !defined (CS_MODE)
skip_flush = dwb_is_created ();
#endif
for (page_id = start_pageid; page_id < npages + start_pageid; page_id++)
{
#if !defined(CS_MODE)
/* check for interrupts from user (i.e. Ctrl-C) */
if ((page_id % FILEIO_CHECK_FOR_INTERRUPT_INTERVAL) == 0)
{
if (logtb_get_check_interrupt (thread_p) && pgbuf_is_log_check_for_interrupts (thread_p))
{
return NULL;
}
}
#endif /* !CS_MODE */
#if !defined(NDEBUG)
/* skip volume header page to find abnormal update */
if (page_id == 0)
{
continue;
}
#endif
if (fileio_write_or_add_to_dwb (thread_p, vol_fd, io_page_p, page_id, page_size, ensure_metadata) == NULL)
{
return NULL;
}
#if defined (SERVER_MODE)
if (kbytes_to_be_written_per_sec > 0 && (page_id + 1) % count_of_page_for_a_sleep == 0)
{
tsc_getticks (&end_tick);
tsc_elapsed_time_usec (&tv_diff, end_tick, start_tick);
previous_elapsed_millis = (tv_diff.tv_sec * 1000LL) + (tv_diff.tv_usec / 1000LL);
/* calculate time to sleep through subtracting */
time_to_sleep = allowed_millis_for_a_sleep - previous_elapsed_millis;
if (time_to_sleep > 0)
{
thread_sleep ((double) time_to_sleep);
}
tsc_getticks (&start_tick);
}
#endif
}
return io_page_p;
}
/*
* fileio_open () - Same as Unix open, but with retry during interrupts
* return: volume descriptor identifier on success, NULL_VOLDES on failure
* vlabel(in): Volume label
* flags(in): open the volume as specified by the flags
* mode(in): used when the volume is created
*/
int
fileio_open (const char *vol_label_p, int flags, int mode)
{
int vol_fd;
do
{
#if defined(WINDOWS)
vol_fd = open (vol_label_p, flags | _O_BINARY, mode);
#else /* WINDOWS */
vol_fd = open (vol_label_p, flags, mode);
#endif /* WINDOWS */
}
while (vol_fd == NULL_VOLDES && errno == EINTR);
#if !defined(WINDOWS)
if (vol_fd > NULL_VOLDES)
{
int high_vol_fd;
int range = MAX_NTRANS + 10;
/* move fd to the over max_clients range */
high_vol_fd = fcntl (vol_fd, F_DUPFD, range);
if (high_vol_fd != -1)
{
close (vol_fd);
vol_fd = high_vol_fd;
}
}
if (prm_get_bool_value (PRM_ID_DBFILES_PROTECT) == true && vol_fd > 0)
{
fileio_get_lock (vol_fd, vol_label_p);
}
#endif /* !WINDOWS */
return vol_fd;
}
#if !defined(WINDOWS)
/*
* fileio_set_permission () -
* return:
* vlabel(in):
*/
int
fileio_set_permission (const char *vol_label_p)
{
int mode;
struct stat buf;
int error = NO_ERROR;
if (stat (vol_label_p, &buf) < 0)
{
error = ER_IO_CANNOT_GET_PERMISSION;
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, vol_label_p);
return error;
}
/* get currently set mode */
mode = buf.st_mode;
/* remove group execute permission from mode */
mode &= ~(S_IEXEC >> 3);
/* set 'set group id bit' in mode */
mode |= S_ISGID;
if (chmod (vol_label_p, mode) < 0)
{
error = ER_IO_CANNOT_CHANGE_PERMISSION;
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, vol_label_p);
return error;
}
return error;
}
/*
* fileio_get_lock () -
* return:
* fd(in):
* vlabel(in):
*/
static int
fileio_get_lock (int fd, const char *vol_label_p)
{
int error = NO_ERROR;
if (fileio_lock_file_read (fd, 0, SEEK_SET, 0) < 0)
{
error = ER_IO_GET_LOCK_FAIL;
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 2, vol_label_p, fd);
}
return error;
}
/*
* fileio_release_lock () -
* return:
* fd(in):
*/
static int
fileio_release_lock (int fd)
{
int error = NO_ERROR;
if (fileio_unlock_file (fd, 0, SEEK_SET, 0) < 0)
{
error = ER_IO_RELEASE_LOCK_FAIL;
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, fd);
}
return error;
}
#endif /* !WINDOWS */
/*
* fileio_close () - Close the volume associated with the given volume descriptor
* return: void
* vdes(in): Volume descriptor
*/
void
fileio_close (int vol_fd)
{
#if !defined(WINDOWS)
if (prm_get_bool_value (PRM_ID_DBFILES_PROTECT) == true)
{
fileio_release_lock (vol_fd);
}
#endif /* !WINDOWS */
if (close (vol_fd) != 0)
{
er_set_with_oserror (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_IO_DISMOUNT_FAIL, 1,
fileio_get_volume_label_by_fd (vol_fd, PEEK));
}
}
/*
* fileio_create () - Create the volume (or file) without initializing it
* return: volume descriptor identifier on success, NULL_VOLDES on failure
* db_fullname(in): Name of the database where the volume belongs
* vlabel(in): Volume label
* volid(in): Volume identifier
* dolock(in): Lock the volume from other Unix processes
* dosync(in): synchronize the writes on the volume ?
*/
static int
fileio_create (THREAD_ENTRY * thread_p, const char *db_full_name_p, const char *vol_label_p, VOLID vol_id,
bool is_do_lock, bool is_do_sync)
{
int tmp_vol_desc = NULL_VOLDES;
int vol_fd;
FILEIO_LOCKF_TYPE lockf_type = FILEIO_NOT_LOCKF;
#if defined(WINDOWS)
int sh_flag;
#else
int o_sync;
#endif /* WINDOWS */
#if !defined(CS_MODE)
/* Make sure that the volume is not already mounted. if it is, dismount the volume. */
vol_fd = fileio_find_volume_descriptor_with_label (vol_label_p);
if (vol_fd != NULL_VOLDES)
{
fileio_dismount (thread_p, vol_fd);
}
#endif /* !CS_MODE */
#if defined(WINDOWS)
sh_flag = is_do_lock ? _SH_DENYWR : _SH_DENYNO;
vol_fd = _sopen (vol_label_p, FILEIO_DISK_FORMAT_MODE | O_BINARY, sh_flag, FILEIO_DISK_PROTECTION_MODE);
if (vol_fd == NULL_VOLDES)
{
if (sh_flag == _SH_DENYRW && errno != ENOENT)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_LOCKED, 6, vol_label_p, db_full_name_p,
"-", 0, "-", "-");
}
else
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_FORMAT_FAIL, 3, vol_label_p, -1, -1LL);
}
}
#else /* !WINDOWS */
o_sync = (is_do_sync != false) ? O_SYNC : 0;
/* If the file exist make sure that nobody else is using it, before it is truncated */
if (is_do_lock != false)
{
tmp_vol_desc = fileio_open (vol_label_p, O_RDWR | o_sync, 0);
if (tmp_vol_desc != NULL_VOLDES)
{
/* The volume (file) already exist. Make sure that nobody is using it before the old one is destroyed */
lockf_type = fileio_lock (db_full_name_p, vol_label_p, tmp_vol_desc, false);
if (lockf_type == FILEIO_NOT_LOCKF)
{
/* Volume seems to be mounted by someone else */
fileio_close (tmp_vol_desc);
return NULL_VOLDES;
}
}
}
vol_fd = fileio_open (vol_label_p, FILEIO_DISK_FORMAT_MODE | o_sync, FILEIO_DISK_PROTECTION_MODE);
if (vol_fd == NULL_VOLDES)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_FORMAT_FAIL, 3, vol_label_p, -1, -1LL);
}
if (tmp_vol_desc != NULL_VOLDES)
{
if (lockf_type != FILEIO_NOT_LOCKF)
{
fileio_unlock (vol_label_p, tmp_vol_desc, lockf_type);
}
fileio_close (tmp_vol_desc);
}
#endif /* WINDOWS */
perfmon_inc_stat (thread_p, PSTAT_FILE_NUM_CREATES);
if (vol_fd != NULL_VOLDES)
{
#if !defined(WINDOWS)
if (is_do_lock == true)
{
lockf_type = fileio_lock (db_full_name_p, vol_label_p, vol_fd, false);
if (lockf_type == FILEIO_NOT_LOCKF)
{
/* This should not happen, the volume seems to be mounted by someone else */
fileio_dismount (thread_p, vol_fd);
fileio_unformat (thread_p, vol_label_p);
vol_fd = NULL_VOLDES;
return vol_fd;
}
}
#endif /* !WINDOWS */
#if !defined(CS_MODE)
if (fileio_cache (vol_id, vol_label_p, vol_fd, lockf_type) != vol_fd)
{
/* This should not happen, the volume seems to be mounted by someone else */
fileio_dismount (thread_p, vol_fd);
fileio_unformat (thread_p, vol_label_p);
vol_fd = NULL_VOLDES;
return vol_fd;
}
#endif /* !CS_MODE */
}
er_log_debug (ARG_FILE_LINE, "Created volume %s\n", vol_label_p);
return vol_fd;
}
/*
* fileio_create_backup_volume () - CREATE A BACKUP VOLUME (INSURE ENOUGH SPACE EXISTS)
* return: volume descriptor identifier on success, NULL_VOLDES on failure
* db_fullname(in): Name of the database where the volume belongs
* vlabel(in): Volume label
* volid(in): Volume identifier
* dolock(in): Lock the volume from other Unix processes
* dosync(in): synchronize the writes on the volume ?
* atleast_npages(in): minimum number of pages required to be free
*
* Note: Tests to insure that there is at least the minimum requred amount of
* space on the given file system are. Then calls fileio_create to create
* the volume (or file) without initializing it. This is needed for tape
* backups since they are not initialized at all plus saves time w/out
* formatting.
* Note: Space checking does not apply to devices, only files.
*/
static int
fileio_create_backup_volume (THREAD_ENTRY * thread_p, const char *db_full_name_p, const char *vol_label_p, VOLID vol_id,
bool is_do_lock, bool is_do_sync, int atleast_npages)
{
struct stat stbuf;
int num_free;
if (stat (vol_label_p, &stbuf) != -1)
{
#if !defined(WINDOWS)
/* In WINDOWS platform, FIFO is not supported, until now. FIFO must be existent before backup operation is
* executed. */
if (S_ISFIFO (stbuf.st_mode))
{
int vdes;
struct timeval to = { 0, 100000 };
while (true)
{
vdes = fileio_open (vol_label_p, O_WRONLY | O_NONBLOCK, 0200);
if (vdes != NULL_VOLDES)
{
break;
}
if (errno == ENXIO)
{
/* sleep for 100 milli-seconds : consider cs & sa mode */
select (0, NULL, NULL, NULL, &to);
#if !defined(CS_MODE)
if (pgbuf_is_log_check_for_interrupts (thread_p) == true)
{
return NULL_VOLDES;
}
#endif
continue;
}
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_FORMAT_FAIL, 3, vol_label_p, -1, -1LL);
return NULL_VOLDES;
}
return vdes;
}
#endif /* !WINDOWS */
/* If there is not enough space in filesystem, then do not bother opening backup volume */
if (atleast_npages > 0 && S_ISREG (stbuf.st_mode))
{
num_free = fileio_get_number_of_partition_free_pages (vol_label_p, IO_PAGESIZE);
if (num_free < atleast_npages)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_FORMAT_OUT_OF_SPACE, 5, vol_label_p, atleast_npages,
(long long) ((IO_PAGESIZE / 1024) * atleast_npages), num_free,
(long long) ((IO_PAGESIZE / 1024) * num_free));
return NULL_VOLDES;
}
}
}
return (fileio_create (thread_p, db_full_name_p, vol_label_p, vol_id, is_do_lock, is_do_sync));
}
/*
* fileio_format () - Format a volume of npages and mount the volume
* return: volume descriptor identifier on success, NULL_VOLDES on failure
* db_fullname(in): Name of the database where the volume belongs
* vlabel(in): Volume label
* volid(in): Volume identifier
* npages(in): Number of pages
* sweep_clean(in): Clean the newly formatted volume
* dolock(in): Lock the volume from other Unix processes
* dosync(in): synchronize the writes on the volume ?
* kbytes_to_be_written_per_sec : size to add volume per sec
*
* Note: If sweep_clean is true, every page is initialized with recovery
* information. In addition a volume can be optionally locked.
* For example, the active log volume is locked to prevent
* several server processes from accessing the same database.
*/
int
fileio_format (THREAD_ENTRY * thread_p, const char *db_full_name_p, const char *vol_label_p, VOLID vol_id,
DKNPAGES npages, bool is_sweep_clean, bool is_do_lock, bool is_do_sync, size_t page_size,
int kbytes_to_be_written_per_sec, bool reuse_file)
{
int vol_fd;
FILEIO_PAGE *malloc_io_page_p;
off_t offset;
DKNPAGES max_npages;
struct stat buf;
bool is_raw_device = false;
/* Check for bad number of pages...and overflow */
if (npages <= 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_FORMAT_BAD_NPAGES, 2, vol_label_p, npages);
return NULL_VOLDES;
}
if (fileio_is_volume_exist (vol_label_p) == true && reuse_file == false)
{
/* The volume that we are trying to create already exist. Remove it and try again */
if (lstat (vol_label_p, &buf) != 0)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, vol_label_p);
}
if (!S_ISLNK (buf.st_mode))
{
fileio_unformat (thread_p, vol_label_p);
}
else
{
if (stat (vol_label_p, &buf) != 0)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, vol_label_p);
}
is_raw_device = S_ISCHR (buf.st_mode);
}
}
if (is_raw_device)
{
max_npages = (DKNPAGES) VOL_MAX_NPAGES (page_size);
}
else
{
max_npages = fileio_get_number_of_partition_free_pages (vol_label_p, page_size);
}
offset = FILEIO_GET_FILE_SIZE (page_size, npages - 1);
/*
* Make sure that there is enough pages on the given partition before we
* create and initialize the volume.
* We should also check for overflow condition.
*/
if (npages > max_npages || (offset < npages && npages > 1))
{
if (offset < npages)
{
/* Overflow */
offset = FILEIO_GET_FILE_SIZE (page_size, VOL_MAX_NPAGES (page_size));
}
if (max_npages >= 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_FORMAT_OUT_OF_SPACE, 5, vol_label_p, npages, (offset / 1024),
max_npages, FILEIO_GET_FILE_SIZE (page_size / 1024, max_npages));
}
else
{
/* There was an error in fileio_get_number_of_partition_free_pages */
;
}
return NULL_VOLDES;
}
malloc_io_page_p = (FILEIO_PAGE *) malloc (page_size);
if (malloc_io_page_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, page_size);
return NULL_VOLDES;
}
memset ((char *) malloc_io_page_p, 0, page_size);
(void) fileio_initialize_res (thread_p, malloc_io_page_p, (PGLENGTH) page_size);
vol_fd = fileio_create (thread_p, db_full_name_p, vol_label_p, vol_id, is_do_lock, is_do_sync);
FI_TEST (thread_p, FI_TEST_FILE_IO_FORMAT, 0);
if (vol_fd != NULL_VOLDES)
{
if (fileio_synchronize_directory (thread_p, vol_label_p) != NO_ERROR)
{
fileio_dismount (thread_p, vol_fd);
fileio_unformat (thread_p, vol_label_p);
free_and_init (malloc_io_page_p);
return NULL_VOLDES;
}
/* initialize the pages of the volume. */
/* initialize at least two pages, the header page and the last page. in case of is_sweep_clean == true, every
* page of the volume will be written. */
if (fileio_write_or_add_to_dwb (thread_p, vol_fd, malloc_io_page_p, 0, page_size, false) == NULL)
{
if (er_errid () != ER_INTERRUPTED)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_WRITE, 2, 0, vol_id);
}
fileio_dismount (thread_p, vol_fd);
fileio_unformat (thread_p, vol_label_p);
free_and_init (malloc_io_page_p);
return NULL_VOLDES;
}
#if defined(HPUX)
if ((is_sweep_clean == true
&& !fileio_initialize_pages (vol_fd, malloc_io_page_p, npages, page_size, false,
kbytes_to_be_written_per_sec)) ||
(is_sweep_clean == false
&& !fileio_write (vol_fd, malloc_io_page_p, npages - 1, page_size, FILEIO_WRITE_DEFAULT_WRITE)))
#else /* HPUX */
if (!((fileio_write_or_add_to_dwb (thread_p, vol_fd, malloc_io_page_p, npages - 1, page_size, true) ==
malloc_io_page_p) && (is_sweep_clean == false
|| fileio_initialize_pages (thread_p, vol_fd, malloc_io_page_p, 0, npages, page_size,
false,
kbytes_to_be_written_per_sec) == malloc_io_page_p)))
#endif /* !HPUX */
{
/* It is likely that we run of space. The partition where the volume was created has been used since we
* checked above. */
max_npages = fileio_get_number_of_partition_free_pages (vol_label_p, page_size);
if (er_errid () != ER_INTERRUPTED)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_FORMAT_OUT_OF_SPACE, 5, vol_label_p, npages,
(offset / 1024), max_npages, (long long) ((page_size / 1024) * max_npages));
}
fileio_dismount (thread_p, vol_fd);
fileio_unformat (thread_p, vol_label_p);
free_and_init (malloc_io_page_p);
return NULL_VOLDES;
}
}
else
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_BO_CANNOT_CREATE_VOL, 2, vol_label_p, db_full_name_p);
}
free_and_init (malloc_io_page_p);
return vol_fd;
}
#if !defined (CS_MODE)
/*
* fileio_expand_to () - Expand a volume to the given number of pages.
*
* return:
*
* error code or NO_ERROR
*
*
* arguments:
*
* vol_id : Volume identifier
* size_npages : New size in pages
* voltype : temporary or permanent volume type
*
*
* How it works:
*
* A new size for file is provided. The new size is expected to be bigger than current size (with the exception of
* recovery cases). This approach replaced extending by a given size to fix recovery errors (file extension could
* be executed twice).
*
* Enough disk space is checked first before doing extend.
*
* Notes:
*
* Pages are not sweep_clean/initialized if they are part of temporary volumes.
*
* No checking for temporary volumes is performed by this function.
*
* On WINDOWS && SERVER MODE io_mutex lock must be obtained before calling lseek. Otherwise, expanding can
* interfere with fileio_read and fileio_write calls. This caused corruptions in the temporary file, random pages
* being written at the end of file instead of being written at their designated places.
*/
int
fileio_expand_to (THREAD_ENTRY * thread_p, VOLID vol_id, DKNPAGES size_npages, DB_VOLTYPE voltype)
{
int vol_fd;
const char *vol_label_p;
FILEIO_PAGE *io_page_p;
DKNPAGES max_npages;
size_t max_size;
PAGEID start_pageid;
size_t current_size;
PAGEID last_pageid;
size_t new_size;
size_t desired_extend_size;
size_t max_extend_size;
#if defined(WINDOWS) && defined(SERVER_MODE)
int rv;
pthread_mutex_t *io_mutex;
static pthread_mutex_t io_mutex_instance = PTHREAD_MUTEX_INITIALIZER;
#endif /* WINDOWS && SERVER_MODE */
int error_code = NO_ERROR;
assert (size_npages > 0);
vol_fd = fileio_get_volume_descriptor (vol_id);
vol_label_p = fileio_get_volume_label (vol_id, PEEK);
if (vol_fd == NULL_VOLDES || vol_label_p == NULL)
{
assert (false); /* I don't think we can accept this case */
return ER_FAILED;
}
max_npages = fileio_get_number_of_partition_free_pages (vol_label_p, IO_PAGESIZE);
if (max_npages < 0)
{
ASSERT_ERROR_AND_SET (error_code);
return error_code;
}
#if defined(WINDOWS) && defined(SERVER_MODE)
io_mutex = fileio_get_volume_mutex (thread_p, vol_fd);
if (io_mutex == NULL)
{
io_mutex = &io_mutex_instance;
}
rv = pthread_mutex_lock (io_mutex);
if (rv != 0)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return ER_FAILED;
}
#endif /* WINDOWS && SERVER_MODE */
/* get current size */
current_size = lseek (vol_fd, 0, SEEK_END);
#if defined(WINDOWS) && defined(SERVER_MODE)
pthread_mutex_unlock (io_mutex);
#endif /* WINDOWS && SERVER_MODE */
/* safe-guard: current size is rounded to IO_PAGESIZE... unless it crashed during an expand */
assert (!LOG_ISRESTARTED () || (current_size % IO_PAGESIZE) == 0);
/* compute new size */
new_size = ((size_t) size_npages) * IO_PAGESIZE;
if (new_size <= current_size)
{
/* this is possible in limited cases. we cannot link file expand to a page, so sometimes we may expand but volume
* header does not reflect this change. also, once expanded, we don't undo the expansion.
* however next time volume wants to expand (this case), it just notices file is already expanded and all is
* good. */
er_log_debug (ARG_FILE_LINE, "skip extending volume %d with current size %zu to new size %zu\n",
vol_id, current_size, new_size);
return NO_ERROR;
}
/* overflow safety check */
/* is this necessary? we dropped support for 32-bits systems. for now, I'll leave this check */
max_size = ((size_t) VOL_MAX_NPAGES (IO_PAGESIZE)) * IO_PAGESIZE;
new_size = MIN (new_size, max_size);
/* consider disk free space */
desired_extend_size = new_size - current_size;
max_extend_size = ((size_t) max_npages) * IO_PAGESIZE;
if (max_extend_size < desired_extend_size)
{
const size_t ONE_KILO = 1024;
error_code = ER_IO_EXPAND_OUT_OF_SPACE;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_EXPAND_OUT_OF_SPACE, 5, vol_label_p,
desired_extend_size / IO_PAGESIZE, new_size / ONE_KILO, max_npages,
FILEIO_GET_FILE_SIZE (IO_PAGESIZE / ONE_KILO, max_npages));
}
/* init page */
io_page_p = (FILEIO_PAGE *) db_private_alloc (thread_p, IO_PAGESIZE);
if (io_page_p == NULL)
{
/* TBD: remove memory allocation manual checks. */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) IO_PAGESIZE);
return ER_OUT_OF_VIRTUAL_MEMORY;
}
memset (io_page_p, 0, IO_PAGESIZE);
(void) fileio_initialize_res (thread_p, io_page_p, IO_PAGESIZE);
start_pageid = (PAGEID) (current_size / IO_PAGESIZE);
last_pageid = ((PAGEID) (new_size / IO_PAGESIZE) - 1);
if (voltype == DB_TEMPORARY_VOLTYPE)
{
/* Write the last page */
if (fileio_write_or_add_to_dwb (thread_p, vol_fd, io_page_p, last_pageid, IO_PAGESIZE, false) != io_page_p)
{
ASSERT_ERROR_AND_SET (error_code);
}
}
else
{
/* support generic volume only */
assert_release (voltype == DB_PERMANENT_VOLTYPE);
if (fileio_initialize_pages (thread_p, vol_fd, io_page_p, start_pageid, last_pageid - start_pageid + 1,
IO_PAGESIZE, true, -1) == NULL)
{
ASSERT_ERROR_AND_SET (error_code);
}
}
db_private_free (thread_p, io_page_p);
return error_code;
}
#endif /* not CS_MODE */
#if defined(ENABLE_UNUSED_FUNCTION)
/*
* fileio_truncate () - TRUNCATE A TEMPORARY VOLUME
* return: npages
* volid(in): Volume identifier
* npages_to_resize(in): Number of pages to resize
*/
DKNPAGES
fileio_truncate (VOLID vol_id, DKNPAGES npages_to_resize)
{
int vol_fd;
const char *vol_label_p;
off_t length;
bool is_retry = true;
vol_fd = fileio_get_volume_descriptor (vol_id);
vol_label_p = fileio_get_volume_label (vol_id, PEEK);
if (vol_fd == NULL_VOLDES || vol_label_p == NULL)
{
return -1;
}
if (npages_to_resize <= 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_FORMAT_BAD_NPAGES, 2, vol_label_p, npages_to_resize);
return -1;
}
length = FILEIO_GET_FILE_SIZE (IO_PAGESIZE, npages_to_resize);
while (is_retry == true)
{
is_retry = false;
if (ftruncate (vol_fd, length))
{
if (errno == EINTR)
{
is_retry = true;
}
else
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_TRUNCATE, 2, npages_to_resize,
fileio_get_volume_label_by_fd (vol_fd, PEEK));
return -1;
}
}
}
return npages_to_resize;
}
#endif
/*
* fileio_unformat () - DESTROY A VOLUME
* return: void
* vlabel(in): Label of volume to unformat
*
* Note: If the volume is mounted, it is dismounted. Then, the volume is
* destroyed/unformatted.
*/
void
fileio_unformat (THREAD_ENTRY * thread_p, const char *vol_label_p)
{
fileio_unformat_and_rename (thread_p, vol_label_p, NULL);
}
/*
* fileio_unformat_and_rename () - DESTROY A VOLUME
* return: void
* vol_label(in): Label of volume to unformat
* new_vlabel(in): New volume label. if NULL, volume will be deleted
*
* Note: If the volume is mounted, it is dismounted. Then, the volume is
* destroyed/unformatted.
*/
void
fileio_unformat_and_rename (THREAD_ENTRY * thread_p, const char *vol_label_p, const char *new_label_p)
{
#if defined (EnableThreadMonitoring)
TSC_TICKS start_tick, end_tick;
TSCTIMEVAL elapsed_time;
#endif
#if !defined(CS_MODE)
int vol_fd;
char vlabel_p[PATH_MAX];
/* Dismount the volume if it is mounted */
vol_fd = fileio_find_volume_descriptor_with_label (vol_label_p);
if (vol_fd != NULL_VOLDES)
{
/* if vol_label_p is a pointer of global vinfo->vlabel, It can be reset in fileio_dismount */
strcpy (vlabel_p, vol_label_p);
vol_label_p = vlabel_p;
fileio_dismount (thread_p, vol_fd);
}
#endif /* !CS_MODE */
#if defined (EnableThreadMonitoring)
if (0 < prm_get_integer_value (PRM_ID_MNT_WAITING_THREAD))
{
tsc_getticks (&start_tick);
}
#endif
if (new_label_p == NULL)
{
(void) remove (vol_label_p);
}
else
{
if (os_rename_file (vol_label_p, new_label_p) != NO_ERROR)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_RENAME_FAIL, 2, vol_label_p, new_label_p);
}
}
#if defined (EnableThreadMonitoring)
if (0 < prm_get_integer_value (PRM_ID_MNT_WAITING_THREAD))
{
tsc_getticks (&end_tick);
tsc_elapsed_time_usec (&elapsed_time, end_tick, start_tick);
}
if (MONITOR_WAITING_THREAD (elapsed_time))
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_MNT_WAITING_THREAD, 3, __func__,
prm_get_integer_value (PRM_ID_MNT_WAITING_THREAD), TO_MSEC (elapsed_time));
}
#endif
er_log_debug (ARG_FILE_LINE, "Destroyed volume %s\n", vol_label_p);
}
/*
* fileio_copy_volume () - COPY A DISK
* return: volume descriptor identifier on success, NULL_VOLDES on failure
* from_vdes(in): From Volume descriptor
* npages(in): From Volume descriptor
* to_vlabel(in): To Volume label
* to_volid(in): Volume identifier assigned to the copy
* reset_rcvinfo(in): Reset recovery information?
*
* Note: Format a new volume with the number of given pages and copy
* the contents of the volume associated with from_vdes onto the
* new generated volume. The recovery information kept in every
* page may be optionally initialized.
*/
int
fileio_copy_volume (THREAD_ENTRY * thread_p, int from_vol_desc, DKNPAGES npages, const char *to_vol_label_p,
VOLID to_vol_id, bool is_reset_recovery_info)
{
PAGEID page_id;
FILEIO_PAGE *malloc_io_page_p = NULL;
int to_vol_desc;
/*
* Create the to_volume. Don't initialize the volume with recovery
* information since it generated/created when the content of the pages are
* copied.
*/
#if defined(HPUX)
/* HP-UX shows the poor performance in fileio_format(). */
to_vol_desc = fileio_create (NULL, to_vol_label_p, to_vol_id, false, false);
#else /* HPUX */
to_vol_desc =
fileio_format (thread_p, NULL, to_vol_label_p, to_vol_id, npages, false, false, false, IO_PAGESIZE, 0, false);
#endif /* HPUX */
if (to_vol_desc == NULL_VOLDES)
{
return NULL_VOLDES;
}
/* Don't read the pages from the page buffer pool but directly from disk */
malloc_io_page_p = (FILEIO_PAGE *) malloc (IO_PAGESIZE);
if (malloc_io_page_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) IO_PAGESIZE);
goto error;
}
if (is_reset_recovery_info == false)
{
/* Copy the volume as it is */
for (page_id = 0; page_id < npages; page_id++)
{
if (fileio_read (thread_p, from_vol_desc, malloc_io_page_p, page_id, IO_PAGESIZE) == NULL
|| fileio_write_or_add_to_dwb (thread_p, to_vol_desc, malloc_io_page_p, page_id, IO_PAGESIZE,
false) == NULL)
{
goto error;
}
}
}
else
{
/* Reset the recovery information. Just like if this was a formatted volume */
for (page_id = 0; page_id < npages; page_id++)
{
if (fileio_read (thread_p, from_vol_desc, malloc_io_page_p, page_id, IO_PAGESIZE) == NULL)
{
goto error;
}
else
{
fileio_reset_page_lsa (malloc_io_page_p, IO_PAGESIZE);
if (fileio_write_or_add_to_dwb (thread_p, to_vol_desc, malloc_io_page_p, page_id, IO_PAGESIZE, false) ==
NULL)
{
goto error;
}
}
}
}
#if !defined(CS_MODE)
if (dwb_synchronize (thread_p, to_vol_desc, to_vol_label_p) != to_vol_desc)
#else
if (fileio_synchronize (thread_p, to_vol_desc, to_vol_label_p, true) != to_vol_desc)
#endif
{
goto error;
}
free_and_init (malloc_io_page_p);
return to_vol_desc;
error:
fileio_dismount (thread_p, to_vol_desc);
fileio_unformat (thread_p, to_vol_label_p);
if (malloc_io_page_p != NULL)
{
free_and_init (malloc_io_page_p);
}
return NULL_VOLDES;
}
/*
* fileio_reset_volume () - Reset the recovery information (LSA) of all pages of given
* volume with given reset_lsa
* return:
* vdes(in): Volume descriptor
* vlabel(in): Volume label
* npages(in): Number of pages of volume to reset
* reset_lsa(in): The reset recovery information LSA
*/
int
fileio_reset_volume (THREAD_ENTRY * thread_p, int vol_fd, const char *vlabel, DKNPAGES npages,
const LOG_LSA * reset_lsa_p)
{
PAGEID page_id;
FILEIO_PAGE *malloc_io_page_p;
int success = NO_ERROR;
bool skip_flush = false;
malloc_io_page_p = (FILEIO_PAGE *) malloc (IO_PAGESIZE);
if (malloc_io_page_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) IO_PAGESIZE);
return ER_FAILED;
}
for (page_id = 0; page_id < npages; page_id++)
{
if (fileio_read (thread_p, vol_fd, malloc_io_page_p, page_id, IO_PAGESIZE) != NULL)
{
fileio_set_page_lsa (malloc_io_page_p, reset_lsa_p, IO_PAGESIZE);
if (fileio_write_or_add_to_dwb (thread_p, vol_fd, malloc_io_page_p, page_id, IO_PAGESIZE, false) == NULL)
{
success = ER_FAILED;
break;
}
}
else
{
success = ER_FAILED;
break;
}
}
free_and_init (malloc_io_page_p);
#if !defined(CS_MODE)
if (dwb_synchronize (thread_p, vol_fd, vlabel) != vol_fd)
#else
if (fileio_synchronize (thread_p, vol_fd, vlabel, true) != vol_fd)
#endif
{
success = ER_FAILED;
}
return success;
}
/*
* fileio_mount () - Mount the volume associated with the given name and permanent
* identifier
* return: volume descriptor identifier on success, NULL_VOLDES on failure
* db_fullname(in): Name of the database where the volume belongs
* vlabel(in): Volume label
* volid(in): Permanent Volume identifier
* lockwait(in): Lock the volume from other Unix processes
* dosync(in): synchronize the writes on the volume ?
*/
int
fileio_mount (THREAD_ENTRY * thread_p, const char *db_full_name_p, const char *vol_label_p, VOLID vol_id, int lock_wait,
bool is_do_sync)
{
#if defined(WINDOWS)
int vol_fd;
int sh_flags;
#if !defined(CS_MODE)
FILEIO_CHECK_AND_INITIALIZE_VOLUME_HEADER_CACHE (NULL_VOLDES);
/* Is volume already mounted ? */
vol_fd = fileio_find_volume_descriptor_with_label (vol_label_p);
if (vol_fd != NULL_VOLDES)
{
return vol_fd;
}
#endif /* !CS_MODE */
sh_flags = lock_wait > 0 ? _SH_DENYWR : _SH_DENYNO;
vol_fd = _sopen (vol_label_p, _O_RDWR | _O_BINARY, sh_flags, 0600);
if (vol_fd == NULL_VOLDES)
{
if (sh_flags == _SH_DENYWR && errno != ENOENT)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_LOCKED, 6, vol_label_p, db_full_name_p,
"-", 0, "-", "-");
}
else
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, vol_label_p);
}
return NULL_VOLDES;
}
#if !defined(CS_MODE)
/* Cache mounting information */
if (fileio_cache (vol_id, vol_label_p, vol_fd, FILEIO_NOT_LOCKF) != vol_fd)
{
fileio_dismount (thread_p, vol_fd);
return NULL_VOLDES;
}
#endif /* !CS_MODE */
return vol_fd;
#else /* WINDOWS */
int vol_fd;
int o_sync;
FILEIO_LOCKF_TYPE lockf_type = FILEIO_NOT_LOCKF;
bool is_do_wait;
struct stat stat_buf;
time_t last_modification_time = 0;
off_t last_size = 0;
#if !defined(CS_MODE)
FILEIO_CHECK_AND_INITIALIZE_VOLUME_HEADER_CACHE (NULL_VOLDES);
/* Is volume already mounted ? */
vol_fd = fileio_find_volume_descriptor_with_label (vol_label_p);
if (vol_fd != NULL_VOLDES)
{
return vol_fd;
}
#endif /* !CS_MODE */
o_sync = (is_do_sync != false) ? O_SYNC : 0;
/* OPEN THE DISK VOLUME PARTITION OR FILE SIMULATED VOLUME */
start:
vol_fd = fileio_open (vol_label_p, O_RDWR | o_sync, 0600);
if (vol_fd == NULL_VOLDES)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, vol_label_p);
return NULL_VOLDES;
}
is_do_wait = (lock_wait > 1) ? true : false;
if (is_do_wait)
{
if (fstat (vol_fd, &stat_buf) != 0)
{
fileio_close (vol_fd);
return NULL_VOLDES;
}
last_modification_time = stat_buf.st_mtime;
last_size = stat_buf.st_size;
}
#if _POSIX_C_SOURCE >= 200112L
if (vol_id >= LOG_DBFIRST_VOLID && prm_get_integer_value (PRM_ID_DATA_FILE_ADVISE) != 0)
{
int advise_flag = 0;
off_t amount = 0; /* entire volume */
switch (prm_get_integer_value (PRM_ID_DATA_FILE_ADVISE))
{
case 1:
advise_flag = POSIX_FADV_NORMAL;
break;
case 2:
advise_flag = POSIX_FADV_SEQUENTIAL;
break;
case 3:
advise_flag = POSIX_FADV_RANDOM;
break;
case 4:
advise_flag = POSIX_FADV_NOREUSE;
break;
case 5:
advise_flag = POSIX_FADV_WILLNEED;
break;
case 6:
advise_flag = POSIX_FADV_DONTNEED;
break;
}
if (posix_fadvise (vol_fd, 0, amount, advise_flag) != 0)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, vol_label_p);
return NULL_VOLDES;
}
}
#endif /* _POSIX_C_SOURCE >= 200112L */
/* LOCK THE DISK */
if (lock_wait != 0)
{
lockf_type = fileio_lock (db_full_name_p, vol_label_p, vol_fd, is_do_wait);
if (lockf_type == FILEIO_NOT_LOCKF)
{
/* Volume seems to be mounted by someone else */
fileio_close (vol_fd);
return NULL_VOLDES;
}
else if (lockf_type == FILEIO_LOCKF && is_do_wait == true)
{
/* may need to reopen the file */
if (fstat (vol_fd, &stat_buf) != 0)
{
fileio_dismount (thread_p, vol_fd);
return NULL_VOLDES;
}
if (last_modification_time != stat_buf.st_mtime || last_size != stat_buf.st_size)
{
/* somebody changed the file before the file lock was acquired */
fileio_dismount (thread_p, vol_fd);
goto start;
}
}
}
#if !defined(CS_MODE)
/* Cache mounting information */
if (fileio_cache (vol_id, vol_label_p, vol_fd, lockf_type) != vol_fd)
{
fileio_dismount (thread_p, vol_fd);
return NULL_VOLDES;
}
#endif /* !CS_MODE */
if (prm_get_bool_value (PRM_ID_DBFILES_PROTECT) == true && vol_fd > 0)
{
fileio_set_permission (vol_label_p);
}
return vol_fd;
#endif /* WINDOWS */
}
/*
* fileio_dismount () - Dismount the volume associated with the given volume
* descriptor
* return: void
* vdes(in): Volume descriptor
*/
void
fileio_dismount (THREAD_ENTRY * thread_p, int vol_fd)
{
const char *vlabel;
#if !defined(WINDOWS)
FILEIO_LOCKF_TYPE lockf_type;
#endif /* !WINDOWS */
/*
* Make sure that all dirty pages of the volume are forced to disk. This
* is needed since a close of a file and program exist, does not imply
* that the dirty pages of the file (or files that the program opened) are
* forced to disk.
*/
vlabel = fileio_get_volume_label_by_fd (vol_fd, PEEK);
#if !defined(CS_MODE)
(void) dwb_synchronize (thread_p, vol_fd, vlabel);
#else
(void) fileio_synchronize (thread_p, vol_fd, vlabel, true);
#endif
#if !defined(WINDOWS)
lockf_type = fileio_get_lockf_type (vol_fd);
if (lockf_type != FILEIO_NOT_LOCKF)
{
fileio_unlock (vlabel, vol_fd, lockf_type);
}
#endif /* !WINDOWS */
fileio_close (vol_fd);
/* Decache volume information even during errors */
fileio_decache (thread_p, vol_fd);
}
/*
* fileio_dismount_without_fsync () -
* return:
* vdes(in):
*/
void
fileio_dismount_without_fsync (THREAD_ENTRY * thread_p, int vol_fd)
{
#if !defined (WINDOWS)
FILEIO_LOCKF_TYPE lockf_type;
lockf_type = fileio_get_lockf_type (vol_fd);
if (lockf_type != FILEIO_NOT_LOCKF)
{
fileio_unlock (fileio_get_volume_label_by_fd (vol_fd, PEEK), vol_fd, lockf_type);
}
#endif /* !WINDOWS */
fileio_close (vol_fd);
/* Decache volume information even during errors */
fileio_decache (thread_p, vol_fd);
}
static int
fileio_max_permanent_volumes (int index, int num_permanent_volums)
{
if (index < (num_permanent_volums - 1) / FILEIO_VOLINFO_INCREMENT)
{
return FILEIO_VOLINFO_INCREMENT - 1;
}
else
{
return (num_permanent_volums - 1) % FILEIO_VOLINFO_INCREMENT;
}
}
static int
fileio_min_temporary_volumes (int index, int num_temp_volums, int num_volinfo_array)
{
if (index > (num_volinfo_array - 1 - (num_temp_volums - 1) / FILEIO_VOLINFO_INCREMENT))
{
return 0;
}
else
{
return FILEIO_VOLINFO_INCREMENT - 1 - (num_temp_volums - 1) % FILEIO_VOLINFO_INCREMENT;
}
}
static FILEIO_SYSTEM_VOLUME_INFO *
fileio_traverse_system_volume (THREAD_ENTRY * thread_p, SYS_VOLINFO_APPLY_FN apply_function, APPLY_ARG * arg)
{
FILEIO_SYSTEM_VOLUME_INFO *sys_vol_info_p;
int rv;
rv = pthread_mutex_lock (&fileio_Sys_vol_info_header.mutex);
for (sys_vol_info_p = &fileio_Sys_vol_info_header.anchor;
sys_vol_info_p != NULL && sys_vol_info_p->vdes != NULL_VOLDES; sys_vol_info_p = sys_vol_info_p->next)
{
if ((*apply_function) (thread_p, sys_vol_info_p, arg) == true)
{
pthread_mutex_unlock (&fileio_Sys_vol_info_header.mutex);
return sys_vol_info_p;
}
}
pthread_mutex_unlock (&fileio_Sys_vol_info_header.mutex);
return NULL;
}
static FILEIO_VOLUME_INFO *
fileio_traverse_permanent_volume (THREAD_ENTRY * thread_p, VOLINFO_APPLY_FN apply_function, APPLY_ARG * arg)
{
int i, j, max_j;
FILEIO_VOLUME_HEADER *header_p;
FILEIO_VOLUME_INFO *vol_info_p;
header_p = &fileio_Vol_info_header;
for (i = 0; i <= (header_p->next_perm_volid - 1) / FILEIO_VOLINFO_INCREMENT; i++)
{
max_j = fileio_max_permanent_volumes (i, header_p->next_perm_volid);
for (j = 0; j <= max_j; j++)
{
vol_info_p = &header_p->volinfo[i][j];
if ((*apply_function) (thread_p, vol_info_p, arg) == true)
{
return vol_info_p;
}
}
}
return NULL;
}
static FILEIO_VOLUME_INFO *
fileio_reverse_traverse_permanent_volume (THREAD_ENTRY * thread_p, VOLINFO_APPLY_FN apply_function, APPLY_ARG * arg)
{
int i, j, max_j;
FILEIO_VOLUME_HEADER *header_p;
FILEIO_VOLUME_INFO *vol_info_p;
header_p = &fileio_Vol_info_header;
for (i = (header_p->next_perm_volid - 1) / FILEIO_VOLINFO_INCREMENT; i >= 0; i--)
{
max_j = fileio_max_permanent_volumes (i, header_p->next_perm_volid);
for (j = max_j; j >= 0; j--)
{
vol_info_p = &header_p->volinfo[i][j];
if ((*apply_function) (thread_p, vol_info_p, arg) == true)
{
return vol_info_p;
}
}
}
return NULL;
}
static FILEIO_VOLUME_INFO *
fileio_traverse_temporary_volume (THREAD_ENTRY * thread_p, VOLINFO_APPLY_FN apply_function, APPLY_ARG * arg)
{
int i, j, min_j, num_temp_vols;
FILEIO_VOLUME_HEADER *header_p;
FILEIO_VOLUME_INFO *vol_info_p;
header_p = &fileio_Vol_info_header;
num_temp_vols = LOG_MAX_DBVOLID - header_p->next_temp_volid;
for (i = header_p->num_volinfo_array - 1;
i > (header_p->num_volinfo_array - 1
- (num_temp_vols + FILEIO_VOLINFO_INCREMENT - 1) / FILEIO_VOLINFO_INCREMENT); i--)
{
min_j = fileio_min_temporary_volumes (i, num_temp_vols, header_p->num_volinfo_array);
for (j = FILEIO_VOLINFO_INCREMENT - 1; j >= min_j; j--)
{
vol_info_p = &header_p->volinfo[i][j];
if ((*apply_function) (thread_p, vol_info_p, arg) == true)
{
return vol_info_p;
}
}
}
return NULL;
}
static FILEIO_VOLUME_INFO *
fileio_reverse_traverse_temporary_volume (THREAD_ENTRY * thread_p, VOLINFO_APPLY_FN apply_function, APPLY_ARG * arg)
{
int i, j, min_j, num_temp_vols;
FILEIO_VOLUME_HEADER *header_p;
FILEIO_VOLUME_INFO *vol_info_p;
header_p = &fileio_Vol_info_header;
num_temp_vols = (LOG_MAX_DBVOLID) - header_p->next_temp_volid;
for (i = (header_p->num_volinfo_array - ((num_temp_vols + FILEIO_VOLINFO_INCREMENT - 1) / FILEIO_VOLINFO_INCREMENT));
i < header_p->num_volinfo_array; i++)
{
min_j = fileio_min_temporary_volumes (i, num_temp_vols, header_p->num_volinfo_array);
for (j = min_j; j < FILEIO_VOLINFO_INCREMENT; j++)
{
vol_info_p = &header_p->volinfo[i][j];
if ((*apply_function) (thread_p, vol_info_p, arg) == true)
{
return vol_info_p;
}
}
}
return NULL;
}
static bool
fileio_dismount_volume (THREAD_ENTRY * thread_p, FILEIO_VOLUME_INFO * vol_info_p, APPLY_ARG * ignore_arg)
{
if (vol_info_p->vdes != NULL_VOLDES)
{
#if !defined(CS_MODE)
(void) dwb_synchronize (thread_p, vol_info_p->vdes, vol_info_p->vlabel);
#else
(void) fileio_synchronize (thread_p, vol_info_p->vdes, vol_info_p->vlabel, true);
#endif
#if !defined(WINDOWS)
if (vol_info_p->lockf_type != FILEIO_NOT_LOCKF)
{
fileio_unlock (vol_info_p->vlabel, vol_info_p->vdes, vol_info_p->lockf_type);
}
#endif /* !WINDOWS */
fileio_close (vol_info_p->vdes);
}
#if defined(WINDOWS) && defined(SERVER_MODE)
pthread_mutex_destroy (&vol_info_p->vol_mutex);
#endif /* WINDOWS && SERVER_MODE */
return false;
}
/*
* fileio_dismount_all () - Dismount all mounted volumes
* return: void
*/
void
fileio_dismount_all (THREAD_ENTRY * thread_p)
{
FILEIO_SYSTEM_VOLUME_HEADER *sys_header_p;
FILEIO_SYSTEM_VOLUME_INFO *sys_vol_info_p, *tmp_sys_vol_info_p;
FILEIO_VOLUME_HEADER *vol_header_p;
int i, num_perm_vols, num_temp_vols;
int rv;
APPLY_ARG ignore_arg = { 0 };
/* First, traverse sys volumes */
sys_header_p = &fileio_Sys_vol_info_header;
rv = pthread_mutex_lock (&sys_header_p->mutex);
for (sys_vol_info_p = &sys_header_p->anchor; sys_vol_info_p != NULL;)
{
if (sys_vol_info_p->vdes != NULL_VOLDES)
{
/* System volume. No need to sync DWB. */
(void) fileio_synchronize (thread_p, sys_vol_info_p->vdes, sys_vol_info_p->vlabel, true);
#if !defined(WINDOWS)
if (sys_vol_info_p->lockf_type != FILEIO_NOT_LOCKF)
{
fileio_unlock (sys_vol_info_p->vlabel, sys_vol_info_p->vdes, sys_vol_info_p->lockf_type);
}
#endif /* !WINDOWS */
fileio_close (sys_vol_info_p->vdes);
}
tmp_sys_vol_info_p = sys_vol_info_p;
sys_vol_info_p = sys_vol_info_p->next;
if (tmp_sys_vol_info_p != &sys_header_p->anchor)
{
#if defined(SERVER_MODE) && defined(WINDOWS)
pthread_mutex_destroy (&tmp_sys_vol_info_p->sysvol_mutex);
#endif /* WINDOWS */
free_and_init (tmp_sys_vol_info_p);
}
}
pthread_mutex_unlock (&sys_header_p->mutex);
/* Second, traverse perm/temp volumes */
vol_header_p = &fileio_Vol_info_header;
rv = pthread_mutex_lock (&vol_header_p->mutex);
num_perm_vols = vol_header_p->next_perm_volid;
(void) fileio_traverse_permanent_volume (thread_p, fileio_dismount_volume, &ignore_arg);
vol_header_p->max_perm_vols = 0;
vol_header_p->next_perm_volid = 0;
num_temp_vols = LOG_MAX_DBVOLID - vol_header_p->next_temp_volid;
(void) fileio_traverse_temporary_volume (thread_p, fileio_dismount_volume, &ignore_arg);
vol_header_p->max_temp_vols = 0;
vol_header_p->next_temp_volid = LOG_MAX_DBVOLID;
if (vol_header_p->volinfo != NULL)
{
for (i = 0; i <= (VOLID_MAX - 1) / FILEIO_VOLINFO_INCREMENT; i++)
{
if (vol_header_p->volinfo[i] != NULL)
{
free_and_init (vol_header_p->volinfo[i]);
}
}
}
free_and_init (vol_header_p->volinfo);
pthread_mutex_unlock (&vol_header_p->mutex);
}
/*
* fileio_map_mounted () - Map over the data volumes
* return:
* fun(in): Function to call on volid and args
* args(in): argumemts for fun
*
* Note : Map over all data volumes (i.e., the log volumes are skipped),
* by calling the given function on every volume. If the function
* returns false the mapping is stopped.
*/
bool
fileio_map_mounted (THREAD_ENTRY * thread_p, bool (*fun) (THREAD_ENTRY * thread_p, VOLID vol_id, void *args),
void *args)
{
FILEIO_VOLUME_INFO *vol_info_p;
FILEIO_VOLUME_HEADER *header_p;
int i, j, max_j, min_j, num_temp_vols;
FILEIO_CHECK_AND_INITIALIZE_VOLUME_HEADER_CACHE (false);
header_p = &fileio_Vol_info_header;
for (i = 0; i <= (header_p->next_perm_volid - 1) / FILEIO_VOLINFO_INCREMENT; i++)
{
max_j = fileio_max_permanent_volumes (i, header_p->next_perm_volid);
for (j = 0; j <= max_j; j++)
{
vol_info_p = &header_p->volinfo[i][j];
if (vol_info_p->vdes != NULL_VOLDES)
{
if (((*fun) (thread_p, vol_info_p->volid, args)) == false)
{
return false;
}
}
}
}
num_temp_vols = LOG_MAX_DBVOLID - header_p->next_temp_volid;
for (i = header_p->num_volinfo_array - 1;
i > (header_p->num_volinfo_array - 1
- (num_temp_vols + FILEIO_VOLINFO_INCREMENT - 1) / FILEIO_VOLINFO_INCREMENT); i--)
{
min_j = fileio_min_temporary_volumes (i, num_temp_vols, header_p->num_volinfo_array);
for (j = FILEIO_VOLINFO_INCREMENT - 1; j >= min_j; j--)
{
vol_info_p = &header_p->volinfo[i][j];
if (vol_info_p->vdes != NULL_VOLDES)
{
if (((*fun) (thread_p, vol_info_p->volid, args)) == false)
{
return false;
}
}
}
}
return true;
}
static bool
fileio_is_volume_descriptor_equal (THREAD_ENTRY * thread_p, FILEIO_VOLUME_INFO * vol_info_p, APPLY_ARG * arg)
{
if (vol_info_p->vdes == NULL_VOLDES)
{
return false;
}
return (vol_info_p->vdes == arg->vdes);
}
static bool
fileio_is_volume_id_equal (THREAD_ENTRY * thread_p, FILEIO_VOLUME_INFO * vol_info_p, APPLY_ARG * arg)
{
if (vol_info_p->volid == NULL_VOLID)
{
return false;
}
return (vol_info_p->volid == arg->vol_id);
}
static bool
fileio_is_volume_id_gt (THREAD_ENTRY * thread_p, FILEIO_VOLUME_INFO * vol_info_p, APPLY_ARG * arg)
{
if (vol_info_p->volid == NULL_VOLID)
{
return false;
}
return (vol_info_p->volid > arg->vol_id);
}
static bool
fileio_is_volume_id_lt (THREAD_ENTRY * thread_p, FILEIO_VOLUME_INFO * vol_info_p, APPLY_ARG * arg)
{
if (vol_info_p->volid == NULL_VOLID)
{
return false;
}
return (vol_info_p->volid < arg->vol_id);
}
static FILEIO_SYSTEM_VOLUME_INFO *
fileio_find_system_volume (THREAD_ENTRY * thread_p, SYS_VOLINFO_APPLY_FN apply_function, APPLY_ARG * arg)
{
FILEIO_SYSTEM_VOLUME_INFO *sys_vol_info_p;
for (sys_vol_info_p = &fileio_Sys_vol_info_header.anchor;
sys_vol_info_p != NULL && sys_vol_info_p->vdes != NULL_VOLDES; sys_vol_info_p = sys_vol_info_p->next)
{
if ((*apply_function) (thread_p, sys_vol_info_p, arg) == true)
{
return sys_vol_info_p;
}
}
return NULL;
}
static bool
fileio_is_system_volume_descriptor_equal (THREAD_ENTRY * thread_p, FILEIO_SYSTEM_VOLUME_INFO * sys_vol_info_p,
APPLY_ARG * arg)
{
return (sys_vol_info_p->vdes == arg->vdes);
}
static bool
fileio_is_system_volume_id_equal (THREAD_ENTRY * thread_p, FILEIO_SYSTEM_VOLUME_INFO * sys_vol_info_p, APPLY_ARG * arg)
{
return (sys_vol_info_p->volid == arg->vol_id);
}
static bool
fileio_is_system_volume_label_equal (THREAD_ENTRY * thread_p, FILEIO_SYSTEM_VOLUME_INFO * sys_vol_info_p,
APPLY_ARG * arg)
{
return (util_compare_filepath (sys_vol_info_p->vlabel, arg->vol_label) == 0);
}
/*
* fileio_page_hexa_dump () - Hexa dump the page.
* return: nothing.
* data(in): source data to dump
* length(in): The length of the data.
*/
void
fileio_page_hexa_dump (const char *src_data, int length)
{
char *log_block_string, *dest_ptr;
const size_t size = length * 4;
int line_no, i;
dest_ptr = log_block_string = (char *) malloc (size);
if (log_block_string == NULL)
{
return;
}
for (i = 0, line_no = 0; i < length; i++, src_data++)
{
if (i % 32 == 0)
{
dest_ptr += sprintf (dest_ptr, "\n%05d: ", line_no++);
}
dest_ptr += sprintf (dest_ptr, "%02X ", (unsigned char) (*src_data));
}
dest_ptr += sprintf (dest_ptr, "\n");
assert ((size_t) (dest_ptr - log_block_string) < size);
er_log_debug (ARG_FILE_LINE, "fileio_page_hexa_dump: data = %s\n", log_block_string);
free_and_init (log_block_string);
}
#if !defined (WINDOWS)
/*
* pwrite_write_with_injected_fault () - Write buffer to file descriptor with fault injection.
* return:
* thread_p(in): thread entry
* fd(in): file descriptor
* buf(in): buffer to write
* count(in): count bytes to write
* offset(in): offset into file
*/
static ssize_t
pwrite_with_injected_fault (THREAD_ENTRY * thread_p, int fd, const void *buf, size_t count, off_t offset)
{
static bool init = false;
const int mod_factor = 25000;
const int block_size = 4096;
int count_blocks;
ssize_t r, written_nbytes;
off_t unit_offset;
bool fi_partial_write1_on, fi_partial_write2_on;
if (init == false)
{
srand ((unsigned int) time (NULL));
init = true;
}
fi_partial_write1_on = FI_INSERTED (FI_TEST_FILE_IO_WRITE_PARTS1);
fi_partial_write2_on = FI_INSERTED (FI_TEST_FILE_IO_WRITE_PARTS2);
if ((fi_partial_write1_on || fi_partial_write2_on) && ((rand () % mod_factor) == 0))
{
// simulate partial write
count_blocks = count / block_size;
written_nbytes = 0;
for (int i = 0; i < count_blocks; i++)
{
if (fi_partial_write1_on)
{
unit_offset = i * block_size;
}
else
{
// reverse order
unit_offset = ((count_blocks - 1) - i) * block_size;
}
r = pwrite (fd, ((char *) buf) + unit_offset, block_size, offset + unit_offset);
written_nbytes += r;
if (r != block_size)
{
return written_nbytes;
}
// randomly exits to remain page is partially written
if ((rand () % count_blocks - 1) == 0)
{
char msg[1024];
char *vlabel;
vlabel = fileio_get_volume_label_by_fd (fd, PEEK);
sprintf (msg, "fault injected to write a page to offset (%ld) of '%s'\n", offset,
vlabel ? vlabel : "unknown volume");
er_print_callstack (ARG_FILE_LINE, "FAULT INJECTION: RANDOM EXIT\n");
er_set (ER_NOTIFICATION_SEVERITY, ARG_FILE_LINE, ER_FAILED_ASSERTION, 1, msg);
// exit handler
(void) fileio_synchronize (thread_p, fd, vlabel, false);
#if !defined(NDEBUG)
if (prm_get_bool_value (PRM_ID_ER_LOG_DEBUG))
{
fileio_page_hexa_dump ((const char *) buf, count);
#if defined (SERVER_MODE) || defined (SA_MODE)
/* Verify page correctness before the crash, for proper recovery purpose. */
if (fileio_is_permanent_volume_descriptor (thread_p, fd))
{
/* Permanent data volume. */
int error_code;
bool is_page_corrupted;
error_code = fileio_page_check_corruption (thread_p, (FILEIO_PAGE *) buf, &is_page_corrupted);
assert (error_code == NO_ERROR && is_page_corrupted == false);
}
else
{
/* sys volume ? */
int rv;
FILEIO_SYSTEM_VOLUME_INFO *sys_volinfo;
APPLY_ARG arg = { 0 };
rv = pthread_mutex_lock (&fileio_Sys_vol_info_header.mutex);
arg.vdes = fd;
sys_volinfo =
fileio_find_system_volume (thread_p, fileio_is_system_volume_descriptor_equal, &arg);
pthread_mutex_unlock (&fileio_Sys_vol_info_header.mutex);
if (sys_volinfo)
{
logpb_debug_check_log_page (thread_p, (void *) buf);
}
}
#endif /* defined (SERVER_MODE) || (SA_MODE) */
}
#endif /* defined (NDEBUG) */
// exit
_exit (0);
}
}
return written_nbytes;
}
return pwrite (fd, buf, count, offset);
}
#endif
#if defined(HPUX) && !defined(IA64)
/*
* pread () -
* return:
* fd(in):
* buf(in):
* nbytes(in):
* offset(in):
*
* Note: Like HP-UX 11, the positioned I/O may not directly available in some
* systems. In that case, use the following simulated positioned I/O
* routines.
*/
ssize_t
pread (int fd, void *buf, size_t nbytes, off_t offset)
{
struct aiocb io;
const struct aiocb *list[1];
int err;
io.aio_fildes = fd;
io.aio_offset = offset;
io.aio_buf = buf;
io.aio_nbytes = nbytes;
io.aio_reqprio = 0;
io.aio_sigevent.sigev_notify = SIGEV_NONE;
err = aio_read (&io); /* atomically reads at offset */
if (err != 0)
{
return (err);
}
list[0] = &io;
err = aio_suspend (list, 1, NULL); /* wait for IO to complete */
if (err != 0)
{
return (err);
}
return aio_return (&io);
}
/*
* pwrite () -
* return:
* fd(in):
* buf(in):
* nbytes(in):
* offset(in):
*/
ssize_t
pwrite (int fd, const void *buf, size_t nbytes, off_t offset)
{
struct aiocb io;
const struct aiocb *list[1];
int err;
io.aio_fildes = fd;
io.aio_offset = offset;
io.aio_buf = buf;
io.aio_nbytes = nbytes;
io.aio_reqprio = 0;
io.aio_sigevent.sigev_notify = SIGEV_NONE;
err = aio_write (&io); /* atomically writes at offset */
if (err != 0)
{
return (err);
}
list[0] = &io;
err = aio_suspend (list, 1, NULL); /* wait for IO to complete */
if (err != 0)
{
return (err);
}
return aio_return (&io);
}
#elif defined(WINDOWS) && defined(SERVER_MODE)
/*
* fileio_get_volume_mutex () - FIND VOLUME MUTEX GIVEN DESCRIPTOR
* return: I/O volume mutex
* vdes(in): Volume descriptor
*/
static pthread_mutex_t *
fileio_get_volume_mutex (THREAD_ENTRY * thread_p, int vdes)
{
FILEIO_VOLUME_INFO *volinfo;
FILEIO_SYSTEM_VOLUME_INFO *sys_volinfo;
int rv;
APPLY_ARG arg = { 0 };
FILEIO_CHECK_AND_INITIALIZE_VOLUME_HEADER_CACHE (NULL);
/* perm/temp volume ? */
arg.vdes = vdes;
volinfo = fileio_traverse_permanent_volume (thread_p, fileio_is_volume_descriptor_equal, &arg);
if (volinfo)
{
return &volinfo->vol_mutex;
}
arg.vdes = vdes;
volinfo = fileio_traverse_temporary_volume (thread_p, fileio_is_volume_descriptor_equal, &arg);
if (volinfo)
{
return &volinfo->vol_mutex;
}
/* sys volume ? */
rv = pthread_mutex_lock (&fileio_Sys_vol_info_header.mutex);
arg.vdes = vdes;
sys_volinfo = fileio_find_system_volume (thread_p, fileio_is_system_volume_descriptor_equal, &arg);
if (sys_volinfo)
{
pthread_mutex_unlock (&fileio_Sys_vol_info_header.mutex);
return &sys_volinfo->sysvol_mutex;
}
pthread_mutex_unlock (&fileio_Sys_vol_info_header.mutex);
return NULL;
}
#endif /* WINDOWS && SERVER_MODE */
/*
* fileio_os_read () - helper for fileio_read
* return: the number of bytes read is returned. On error, error code.
* vol_fd(in): Volume descriptor
* io_page_p(out): Address where content of page is stored. Must be of page_size long
* count(in): the number of bytes to be read
* offset(in): starting file offset
*/
static ssize_t
fileio_os_read (THREAD_ENTRY * thread_p, int vol_fd, void *io_page_p, size_t count, off_t offset)
{
#if !defined (SERVER_MODE)
/* Locate the desired page */
if (lseek (vol_fd, offset, SEEK_SET) != offset)
{
return ER_FAILED;
}
/* Read the desired page */
return read (vol_fd, io_page_p, count);
#elif defined (WINDOWS)
// TODO: replace it with ReadFile
ssize_t nbytes;
int rv;
pthread_mutex_t *io_mutex;
static pthread_mutex_t io_mutex_instance = PTHREAD_MUTEX_INITIALIZER;
io_mutex = fileio_get_volume_mutex (thread_p, vol_fd);
if (io_mutex == NULL)
{
io_mutex = &io_mutex_instance;
}
rv = pthread_mutex_lock (io_mutex);
if (rv != 0)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CSS_PTHREAD_MUTEX_LOCK, 0);
return ER_FAILED;
}
/* Locate the desired page */
if (lseek (vol_fd, offset, SEEK_SET) != offset)
{
pthread_mutex_unlock (io_mutex);
return ER_FAILED;
}
/* Read the desired page */
nbytes = read (vol_fd, io_page_p, (unsigned int) count);
if (pthread_mutex_unlock (io_mutex) != 0)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CSS_PTHREAD_MUTEX_UNLOCK, 0);
return ER_FAILED;
}
return nbytes;
#else /* WINDOWS */
return pread (vol_fd, io_page_p, count, offset);
#endif
}
/*
* fileio_read () - READ A PAGE FROM DISK
* return:
* vol_fd(in): Volume descriptor
* io_page_p(out): Address where content of page is stored. Must be of page_size long
* page_id(in): Page identifier
* page_size(in): Page size
*
* Note: Read the content of the page described by page_id onto the
* given io_page_p buffer. The io_page_p must be page_size long.
*/
void *
fileio_read (THREAD_ENTRY * thread_p, int vol_fd, void *io_page_p, PAGEID page_id, size_t page_size)
{
#if defined (EnableThreadMonitoring)
TSC_TICKS start_tick, end_tick;
TSCTIMEVAL elapsed_time;
#endif
off_t offset = FILEIO_GET_FILE_SIZE (page_size, page_id);
ssize_t nbytes;
bool is_retry = true;
#if defined (EnableThreadMonitoring)
if (0 < prm_get_integer_value (PRM_ID_MNT_WAITING_THREAD))
{
tsc_getticks (&start_tick);
}
#endif
while (is_retry == true)
{
is_retry = false;
nbytes = fileio_os_read (thread_p, vol_fd, io_page_p, page_size, offset);
if (nbytes != (ssize_t) page_size)
{
if (nbytes == 0)
{
/* This is an end of file. We are trying to read beyond the allocated disk space */
er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_PB_BAD_PAGEID, 2, page_id,
fileio_get_volume_label_by_fd (vol_fd, PEEK));
return NULL;
}
if (errno == EINTR)
{
is_retry = true;
}
else
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_READ, 2, page_id,
fileio_get_volume_label_by_fd (vol_fd, PEEK));
return NULL;
}
}
}
#if defined (EnableThreadMonitoring)
if (0 < prm_get_integer_value (PRM_ID_MNT_WAITING_THREAD))
{
tsc_getticks (&end_tick);
tsc_elapsed_time_usec (&elapsed_time, end_tick, start_tick);
}
if (MONITOR_WAITING_THREAD (elapsed_time))
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_MNT_WAITING_THREAD, 3, __func__,
prm_get_integer_value (PRM_ID_MNT_WAITING_THREAD), TO_MSEC (elapsed_time));
}
#endif
perfmon_inc_stat (thread_p, PSTAT_FILE_NUM_IOREADS);
return io_page_p;
}
/*
* fileio_write_or_add_to_dwb () - Write a page to disk if DWb disabled, otherwise add it to DWB
* return: io_page_p on success, NULL on failure
* vol_fd(in): Volume descriptor
* io_page_p(in): In-memory address where the current content of page resides
* page_id(in): Page identifier
* page_size(in): Page size
* ensure_metadata(in): Include metadata when syncing.
*
*/
void *
fileio_write_or_add_to_dwb (THREAD_ENTRY * thread_p, int vol_fd, FILEIO_PAGE * io_page_p, PAGEID page_id,
size_t page_size, bool ensure_metadata)
{
#if !defined (CS_MODE)
bool skip_flush = false;
DWB_SLOT *p_dwb_slot = NULL;
VPID vpid;
FILEIO_VOLUME_INFO *vol_info_p;
APPLY_ARG arg = { 0 };
int error_code;
FILEIO_WRITE_MODE write_mode;
assert (vol_fd != NULL_VOLDES && io_page_p != NULL);
skip_flush = dwb_is_created ();
if (skip_flush)
{
FILEIO_CHECK_AND_INITIALIZE_VOLUME_HEADER_CACHE (NULL);
arg.vdes = vol_fd;
vol_info_p = fileio_traverse_permanent_volume (thread_p, fileio_is_volume_descriptor_equal, &arg);
if (vol_info_p)
{
/* Permanent volumes - uses DWB. */
VPID_SET (&vpid, vol_info_p->volid, page_id);
io_page_p->prv.volid = vol_info_p->volid;
io_page_p->prv.pageid = page_id;
error_code = dwb_add_page (thread_p, io_page_p, &vpid, ensure_metadata, &p_dwb_slot);
if (error_code != NO_ERROR)
{
return NULL;
}
else if (p_dwb_slot != NULL)
{
/* The page was successfully added to DWB. It will be written later. */
return io_page_p;
}
/* DWB disabled, write the page. */
}
/* Not permanent volume - write the page. */
}
write_mode = skip_flush ? FILEIO_WRITE_NO_COMPENSATE_WRITE : FILEIO_WRITE_DEFAULT_WRITE;
return fileio_write (thread_p, vol_fd, io_page_p, page_id, page_size, write_mode);
#else
return fileio_write (thread_p, vol_fd, io_page_p, page_id, page_size, FILEIO_WRITE_DEFAULT_WRITE);
#endif
}
/*
* fileio_os_write () - helper for fileio_write
* return: the number of bytes written is returned. On error, error code.
* vol_fd(in): Volume descriptor
* io_page_p(in): In-memory address where the current content of page resides
* count(in): the number of bytes to be written
* offset(in): starting file offset
*
*/
static ssize_t
fileio_os_write (THREAD_ENTRY * thread_p, int vol_fd, void *io_page_p, size_t count, off_t offset)
{
#if !defined (SERVER_MODE)
if (lseek (vol_fd, offset, SEEK_SET) != offset)
{
return ER_FAILED;
}
/* write the page */
return write (vol_fd, io_page_p, count);
#elif defined (WINDOWS)
// TODO: replace it with WriteFile
int rv, nbytes;
pthread_mutex_t *io_mutex;
static pthread_mutex_t io_mutex_instance = PTHREAD_MUTEX_INITIALIZER;
io_mutex = fileio_get_volume_mutex (thread_p, vol_fd);
if (io_mutex == NULL)
{
io_mutex = &io_mutex_instance;
}
rv = pthread_mutex_lock (io_mutex);
if (rv != 0)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CSS_PTHREAD_MUTEX_LOCK, 0);
return ER_FAILED;
}
if (lseek (vol_fd, offset, SEEK_SET) != offset)
{
pthread_mutex_unlock (io_mutex);
return ER_FAILED;
}
/* write the page */
nbytes = write (vol_fd, io_page_p, (unsigned int) count);
pthread_mutex_unlock (io_mutex);
return (ssize_t) nbytes;
#elif defined (NDEBUG)
/* release mode */
return pwrite (vol_fd, io_page_p, count, offset);
#else
/* server debugging mode */
return pwrite_with_injected_fault (thread_p, vol_fd, io_page_p, count, offset);
#endif
}
/*
* fileio_write () - WRITE A PAGE TO DISK
* return: io_page_p on success, NULL on failure
* vol_fd(in): Volume descriptor
* io_page_p(in): In-memory address where the current content of page resides
* page_id(in): Page identifier
* page_size(in): Page size
* write_mode(in): FILEIO_WRITE_NO_COMPENSATE_WRITE skips page flush
*
* Note: Write the content of the page described by page_id to disk. The content of the page is stored onto io_page_p
* buffer which is page_size long.
*/
void *
fileio_write (THREAD_ENTRY * thread_p, int vol_fd, void *io_page_p, PAGEID page_id, size_t page_size,
FILEIO_WRITE_MODE write_mode)
{
#if defined (EnableThreadMonitoring)
TSC_TICKS start_tick, end_tick;
TSCTIMEVAL elapsed_time;
#endif
ssize_t nbytes_written;
off_t offset = FILEIO_GET_FILE_SIZE (page_size, page_id);
bool is_retry = true;
#if defined (EnableThreadMonitoring)
if (0 < prm_get_integer_value (PRM_ID_MNT_WAITING_THREAD))
{
tsc_getticks (&start_tick);
}
#endif
while (is_retry == true)
{
is_retry = false;
nbytes_written = fileio_os_write (thread_p, vol_fd, io_page_p, page_size, offset);
if (nbytes_written != (ssize_t) page_size)
{
if (errno == EINTR)
{
is_retry = true;
}
else if (errno == ENOSPC)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_WRITE_OUT_OF_SPACE, 2, page_id,
fileio_get_volume_label_by_fd (vol_fd, PEEK));
#if defined (SERVER_MODE) && !defined (WINDOWS)
syslog (LOG_ALERT, "[CUBRID] %s () at %s:%d %m", __func__, __FILE__, __LINE__);
#endif
return NULL;
}
else
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_WRITE, 2, page_id,
fileio_get_volume_label_by_fd (vol_fd, PEEK));
return NULL;
}
}
}
#if defined (EnableThreadMonitoring)
if (0 < prm_get_integer_value (PRM_ID_MNT_WAITING_THREAD))
{
tsc_getticks (&end_tick);
tsc_elapsed_time_usec (&elapsed_time, end_tick, start_tick);
}
if (MONITOR_WAITING_THREAD (elapsed_time))
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_MNT_WAITING_THREAD, 3, __func__,
prm_get_integer_value (PRM_ID_MNT_WAITING_THREAD), TO_MSEC (elapsed_time));
}
#endif
if (write_mode == FILEIO_WRITE_DEFAULT_WRITE)
{
fileio_compensate_flush (thread_p, vol_fd, 1);
}
perfmon_inc_stat (thread_p, PSTAT_FILE_NUM_IOWRITES);
return io_page_p;
}
/*
* fileio_read_pages () -
*/
void *
fileio_read_pages (THREAD_ENTRY * thread_p, int vol_fd, char *io_pages_p, PAGEID page_id, int num_pages,
size_t page_size)
{
#if defined (EnableThreadMonitoring)
TSC_TICKS start_tick, end_tick;
TSCTIMEVAL elapsed_time;
#endif
off_t offset;
ssize_t nbytes_read;
size_t nbytes_to_be_read;
assert (num_pages > 0);
offset = FILEIO_GET_FILE_SIZE (page_size, page_id);
nbytes_to_be_read = ((size_t) page_size) * ((size_t) num_pages);
#if defined (EnableThreadMonitoring)
if (0 < prm_get_integer_value (PRM_ID_MNT_WAITING_THREAD))
{
tsc_getticks (&start_tick);
}
#endif
while (nbytes_to_be_read > 0)
{
nbytes_read = fileio_os_read (thread_p, vol_fd, io_pages_p, nbytes_to_be_read, offset);
if (nbytes_read <= 0)
{
if (nbytes_read == 0)
{
return NULL;
}
switch (errno)
{
case EINTR:
case EAGAIN:
continue;
#if !defined(WINDOWS)
case EOVERFLOW:
return NULL;
#endif /* !WINDOWS */
default:
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_READ, 2, page_id,
fileio_get_volume_label_by_fd (vol_fd, PEEK));
return NULL;
}
}
}
offset += nbytes_read;
io_pages_p += nbytes_read;
nbytes_to_be_read -= nbytes_read;
}
#if defined (EnableThreadMonitoring)
if (0 < prm_get_integer_value (PRM_ID_MNT_WAITING_THREAD))
{
tsc_getticks (&end_tick);
tsc_elapsed_time_usec (&elapsed_time, end_tick, start_tick);
}
if (MONITOR_WAITING_THREAD (elapsed_time))
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_MNT_WAITING_THREAD, 3, __func__,
prm_get_integer_value (PRM_ID_MNT_WAITING_THREAD), TO_MSEC (elapsed_time));
}
#endif
perfmon_inc_stat (thread_p, PSTAT_FILE_NUM_IOREADS);
return io_pages_p;
}
/*
* fileio_write_pages () - write the content of several contiguous pages to disk
* return: io_page_p on success, NULL on failure
* thread_p(in): Thread entry
* vol_fd(in): Volume descriptor
* io_page_p(in): In-memory address where the pages resides
* page_id(in): First page identifier
* num_pages(in): Number of pages to flush
* page_size(in): Page size
* write_mode(in): FILEIO_WRITE_NO_COMPENSATE_WRITE skips page flush
*/
void *
fileio_write_pages (THREAD_ENTRY * thread_p, int vol_fd, char *io_pages_p, PAGEID page_id, int num_pages,
size_t page_size, FILEIO_WRITE_MODE write_mode)
{
#if defined (EnableThreadMonitoring)
TSC_TICKS start_tick, end_tick;
TSCTIMEVAL elapsed_time;
#endif
off_t offset;
ssize_t nbytes_written;
size_t nbytes_to_be_written;
assert (num_pages > 0);
offset = FILEIO_GET_FILE_SIZE (page_size, page_id);
nbytes_to_be_written = ((size_t) page_size) * ((size_t) num_pages);
#if defined (EnableThreadMonitoring)
if (0 < prm_get_integer_value (PRM_ID_MNT_WAITING_THREAD))
{
tsc_getticks (&start_tick);
}
#endif
while (nbytes_to_be_written > 0)
{
nbytes_written = fileio_os_write (thread_p, vol_fd, io_pages_p, nbytes_to_be_written, offset);
if (nbytes_written <= 0)
{
if (nbytes_written == 0)
{
return NULL;
}
switch (errno)
{
case EINTR:
case EAGAIN:
continue;
#if !defined(WINDOWS)
case EOVERFLOW:
return NULL;
#endif /* !WINDOWS */
default:
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_WRITE, 2, page_id,
fileio_get_volume_label_by_fd (vol_fd, PEEK));
return NULL;
}
}
}
offset += nbytes_written;
io_pages_p += nbytes_written;
nbytes_to_be_written -= nbytes_written;
}
#if defined (EnableThreadMonitoring)
if (0 < prm_get_integer_value (PRM_ID_MNT_WAITING_THREAD))
{
tsc_getticks (&end_tick);
tsc_elapsed_time_usec (&elapsed_time, end_tick, start_tick);
}
if (MONITOR_WAITING_THREAD (elapsed_time))
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_MNT_WAITING_THREAD, 3, __func__,
prm_get_integer_value (PRM_ID_MNT_WAITING_THREAD), TO_MSEC (elapsed_time));
}
#endif
if (write_mode == FILEIO_WRITE_DEFAULT_WRITE)
{
fileio_compensate_flush (thread_p, vol_fd, num_pages);
}
perfmon_add_stat (thread_p, PSTAT_FILE_NUM_IOWRITES, num_pages);
return io_pages_p;
}
/*
* fileio_writev () - WRITE A SET OF CONTIGUOUS PAGES TO DISK
* return: io_pgptr on success, NULL on failure
* vol_fd(in): Volume descriptor
* arrayof_io_pgptr(in): An array address to address where the current content of pages reside
* start_page_id(in): Page identifier of first page
* npages(in): Number of consecutive pages
* page_size(in): Page size
*
* Note: Write the content of the consecutive pages described by start_pageid to disk. The content of the pages are
* address by the io_pgptr array. Each io_pgptr buffer is page size long.
*
* io_pgptr[0] -->> start_pageid
* io_pgptr[1] -->> start_pageid + 1
* ...
* io_pgptr[npages - 1] -->> start_pageid + npages - 1
*/
void *
fileio_writev (THREAD_ENTRY * thread_p, int vol_fd, void **io_page_array, PAGEID start_page_id, DKNPAGES npages,
size_t page_size)
{
int i;
FILEIO_WRITE_MODE write_mode = FILEIO_WRITE_DEFAULT_WRITE;
#if !defined (CS_MODE)
write_mode = dwb_is_created () == true ? FILEIO_WRITE_NO_COMPENSATE_WRITE : FILEIO_WRITE_DEFAULT_WRITE;
#endif
for (i = 0; i < npages; i++)
{
if (fileio_write (thread_p, vol_fd, io_page_array[i], start_page_id + i, page_size, write_mode) == NULL)
{
return NULL;
}
}
return io_page_array[0];
}
bool
fileio_fsync_pending (void)
{
#if defined (SERVER_MODE)
// *INDENT-OFF*
static std::atomic<uint64_t> counter (0);
// *INDENT-ON*
#else
static uint64_t counter (0);
#endif
uint64_t prev_counter;
int threshold;
threshold = prm_get_integer_value (PRM_ID_SUPPRESS_FSYNC);
if (threshold <= 0)
{
return false;
}
#if defined (SERVER_MODE)
prev_counter = counter.fetch_add (1, std::memory_order_relaxed);
#else
prev_counter = counter++;
#endif
if (!((prev_counter + 1) % (uint64_t) threshold))
{
return false;
}
return true;
}
/*
* fileio_synchronize () - Synchronize a database volume's state with that on disk
* return: vdes or NULL_VOLDES
* vol_fd(in): Volume descriptor
* vlabel(in): Volume label
* ensure_metadata(in): Include metadata when syncing.
*/
int
fileio_synchronize (THREAD_ENTRY * thread_p, int vol_fd, const char *vlabel, bool ensure_metadata)
{
#if defined (EnableThreadMonitoring)
TSC_TICKS start_tick, end_tick;
TSCTIMEVAL elapsed_time;
#endif
int error = NO_ERROR;
/* should fsync ? */
if (fileio_fsync_pending ())
{
return vol_fd;
}
#if defined (EnableThreadMonitoring)
if (0 < prm_get_integer_value (PRM_ID_MNT_WAITING_THREAD))
{
tsc_getticks (&start_tick);
}
#endif
error = ensure_metadata ? fsync (vol_fd) : fdatasync (vol_fd);
#if defined (EnableThreadMonitoring)
if (0 < prm_get_integer_value (PRM_ID_MNT_WAITING_THREAD))
{
tsc_getticks (&end_tick);
tsc_elapsed_time_usec (&elapsed_time, end_tick, start_tick);
}
#endif
if (error != NO_ERROR)
{
/* sync error is not alwasy handled and I am not sure a proper safe handling is possible: raise as fatal error */
er_set_with_oserror (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_SYNC, 1, (vlabel ? vlabel : "Unknown"));
return NULL_VOLDES;
}
#if defined (EnableThreadMonitoring)
if (MONITOR_WAITING_THREAD (elapsed_time))
{
er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_MNT_WAITING_THREAD, 3, __func__,
prm_get_integer_value (PRM_ID_MNT_WAITING_THREAD), TO_MSEC (elapsed_time));
}
#endif
perfmon_inc_stat (thread_p, PSTAT_FILE_NUM_IOSYNCHES);
return vol_fd;
}
/*
* fileio_synchronize_directory () - Synchronize a directory where the volume is located
* return: ER_FAILED or NO_ERROR
* label(in): Volume label
*/
int
fileio_synchronize_directory (THREAD_ENTRY * thread_p, const char *label)
{
char path[PATH_MAX + 1];
int fd;
(void) fileio_get_directory_path (path, label);
/* get the fd for the parent directory */
fd = fileio_open (path, O_RDONLY, FILEIO_DISK_PROTECTION_MODE);
if (fd == NULL_VOLDES)
{
return ER_FAILED;
}
if (fileio_synchronize (thread_p, fd, path, true) == NULL_VOLDES)
{
fileio_close (fd);
return ER_FAILED;
}
fileio_close (fd);
return NO_ERROR;
}
/*
* fileio_synchronize_bg_archive_volume () -
* return:
*/
static int
fileio_synchronize_bg_archive_volume (THREAD_ENTRY * thread_p)
{
APPLY_ARG arg = { 0 };
arg.vol_id = LOG_DBLOG_BG_ARCHIVE_VOLID;
(void) fileio_traverse_system_volume (thread_p, fileio_synchronize_sys_volume, &arg);
return NO_ERROR;
}
/*
* fileio_synchronize_sys_volume () -
* return:
* vol_info_p(in):
*/
static bool
fileio_synchronize_sys_volume (THREAD_ENTRY * thread_p, FILEIO_SYSTEM_VOLUME_INFO * sys_vol_info_p, APPLY_ARG * arg)
{
bool found = false;
if (sys_vol_info_p->vdes != NULL_VOLDES)
{
/* sync when match is found or arg.vol_id is given as NULL_VOLID for all sys volumes. */
if (arg->vol_id == NULL_VOLID)
{
/* fall through */
;
}
else if (sys_vol_info_p->volid == arg->vol_id)
{
found = true;
}
else
{
/* irrelevant volume */
return false;
}
/* System volume. No need to sync DWB. */
fileio_synchronize (thread_p, sys_vol_info_p->vdes, sys_vol_info_p->vlabel, false);
}
return found;
}
/*
* fileio_synchronize_volume () -
* return:
* vol_info_p(in):
*
* Note : This function does not synchronize DWB.
*/
static bool
fileio_synchronize_volume (THREAD_ENTRY * thread_p, FILEIO_VOLUME_INFO * vol_info_p, APPLY_ARG * arg)
{
bool found = false;
if (vol_info_p->vdes != NULL_VOLDES)
{
/* sync when match is found or arg.vol_id is given as NULL_VOLID for all sys volumes. */
if (arg->vol_id == NULL_VOLID)
{
/* fall through */
;
}
else if (vol_info_p->volid == arg->vol_id)
{
found = true;
}
else
{
/* irrelevant volume */
return false;
}
fileio_synchronize (thread_p, vol_info_p->vdes, vol_info_p->vlabel, false);
}
return found;
}
/*
* fileio_synchronize_all () - Synchronize all database volumes with disk
* return:
*/
int
fileio_synchronize_all (THREAD_ENTRY * thread_p)
{
int success = NO_ERROR;
bool all_sync = false;
APPLY_ARG arg = { 0 };
#if defined (SERVER_MODE) || defined (SA_MODE)
PERF_UTIME_TRACKER time_track;
PERF_UTIME_TRACKER_START (thread_p, &time_track);
#endif /* defined (SERVER_MODE) || defined (SA_MODE) */
arg.vol_id = NULL_VOLID;
er_stack_push ();
#if !defined (CS_MODE)
/* Flush DWB before volume data. */
success = dwb_flush_force (thread_p, &all_sync);
#endif
/* Check whether the volumes were flushed. */
if (success == NO_ERROR && all_sync == false)
{
/* Flush volume data. */
(void) fileio_traverse_permanent_volume (thread_p, fileio_synchronize_volume, &arg);
if (er_errid () == ER_IO_SYNC)
{
success = ER_FAILED;
}
}
er_stack_pop ();
#if defined (SERVER_MODE) || defined (SA_MODE)
PERF_UTIME_TRACKER_TIME (thread_p, &time_track, PSTAT_FILE_IOSYNC_ALL);
#endif /* defined (SERVER_MODE) || defined (SA_MODE) */
return success;
}
#if defined(ENABLE_UNUSED_FUNCTION)
/*
* fileio_read_user_area () - READ A PORTION OF THE USER AREA OF THE GIVEN PAGE
* return: area on success, NULL on failure
* vdes(in): Volume descriptor
* pageid(in): Page identifier
* start_offset(in): Start offset of interested content in page
* nbytes(in): Length of the content of page to copy
* area(out):
*
* Note: Copy a portion of the content of the user area of the page described
* by pageid onto the given area. The area must be big enough to hold
* the needed content
*/
void *
fileio_read_user_area (THREAD_ENTRY * thread_p, int vol_fd, PAGEID page_id, off_t start_offset, size_t nbytes,
void *area_p)
{
off_t offset;
bool is_retry = true;
FILEIO_PAGE *io_page_p;
#if defined(WINDOWS) && defined(SERVER_MODE)
pthread_mutex_t io_mutex;
int rv;
int actual_nread;
#endif /* WINDOWS && SERVER_MODE */
io_page_p = (FILEIO_PAGE *) malloc (IO_PAGESIZE);
if (io_page_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) IO_PAGESIZE);
return NULL;
}
/* Find the offset intop the user area on the desired page */
offset = FILEIO_GET_FILE_SIZE (IO_PAGESIZE, page_id);
while (is_retry == true)
{
is_retry = false;
#if !defined(SERVER_MODE)
/* Locate the desired page */
if (lseek (vol_fd, offset, SEEK_SET) != offset)
{
if (io_page_p != NULL)
{
free_and_init (io_page_p);
}
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_READ, 2, page_id,
fileio_get_volume_label (fileio_get_volume_id (vol_fd), PEEK));
return NULL;
}
/* Read the desired page */
if (read (vol_fd, io_page_p, IO_PAGESIZE) != IO_PAGESIZE)
#elif defined(WINDOWS)
io_mutex = fileio_get_volume_mutex (thread_p, vol_fd);
rv = pthread_mutex_lock (&io_mutex);
if (rv != 0)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, rv, 0);
return NULL;
}
/* Locate the desired page */
if (lseek (vol_fd, offset, SEEK_SET) != offset)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_READ, 2, page_id,
fileio_get_volume_label (fileio_get_volume_id (vol_fd), PEEK));
pthread_mutex_unlock (&io_mutex);
return NULL;
}
/* Read the desired page */
actual_nread = read (vol_fd, io_page_p, IO_PAGESIZE);
if (pthread_mutex_unlock (&io_mutex) != 0)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CSS_PTHREAD_MUTEX_UNLOCK, 0);
return NULL;
}
if (actual_nread != IO_PAGESIZE)
#else /* WINDOWS */
if (pread (vol_fd, io_page_p, IO_PAGESIZE, offset) != IO_PAGESIZE)
#endif /* WINDOWS */
{
if (errno == EINTR)
{
is_retry = true;
}
else
{
if (io_page_p != NULL)
{
free_and_init (io_page_p);
}
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_READ, 2, page_id,
fileio_get_volume_label_by_fd (vol_fd, PEEK));
return NULL;
}
}
}
memcpy (area_p, io_page_p->page + start_offset, nbytes);
if (io_page_p != NULL)
{
free_and_init (io_page_p);
}
perfmon_inc_stat (thread_p, PSTAT_FILE_NUM_IOREADS);
return area_p;
}
/*
* fileio_write_user_area () - READ A PORTION OF THE USER AREA OF THE GIVEN PAGE
* return: area on success, NULL on failure
* vdes(in): Volume descriptor
* pageid(in): Page identifier
* start_offset(in): Start offset of interested content in page
* nbytes(in): Length of the content of page to copy
* area(out):
*
* Note: Copy a portion of the content of the user area of the page described
* by pageid onto the given area. The area must be big enough to hold
* the needed content
*/
void *
fileio_write_user_area (THREAD_ENTRY * thread_p, int vol_fd, PAGEID page_id, off_t start_offset, int nbytes,
void *area_p)
{
off_t offset;
bool is_retry = true;
FILEIO_PAGE *io_page_p = NULL;
void *write_p;
struct stat stat_buf;
#if defined(WINDOWS) && defined(SERVER_MODE)
int actual_nwrite, rv;
pthread_mutex_t io_mutex;
#endif /* WINDOWS && SERVER_MODE */
if (fstat (vol_fd, &stat_buf) != 0)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_WRITE, 2, page_id,
fileio_get_volume_label_by_fd (vol_fd, PEEK));
return NULL;
}
#if defined(WINDOWS)
/* Find the offset intop the user area on the desired page */
offset = (FILEIO_GET_FILE_SIZE (IO_PAGESIZE, page_id) + offsetof (FILEIO_PAGE, page));
/* Add the starting offset */
offset += start_offset;
write_p = area_p;
#else /* WINDOWS */
if (S_ISREG (stat_buf.st_mode)) /* regular file */
{
/* Find the offset intop the user area on the desired page */
offset = (FILEIO_GET_FILE_SIZE (IO_PAGESIZE, page_id) + offsetof (FILEIO_PAGE, page));
/* Add the starting offset */
offset += start_offset;
write_p = area_p;
}
else if (S_ISCHR (stat_buf.st_mode)) /* Raw device */
{
offset = FILEIO_GET_FILE_SIZE (IO_PAGESIZE, page_id);
if (nbytes != DB_PAGESIZE)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_WRITE, 2, page_id,
fileio_get_volume_label_by_fd (vol_fd, PEEK));
return NULL;
}
io_page_p = (FILEIO_PAGE *) malloc (IO_PAGESIZE);
if (io_page_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) IO_PAGESIZE);
return NULL;
}
(void) fileio_initialize_res (thread_p, io_page_p, IO_PAGESIZE);
memcpy (io_page_p->page, area_p, nbytes);
write_p = (void *) io_page_p;
nbytes = IO_PAGESIZE;
}
else
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_WRITE, 2, page_id,
fileio_get_volume_label_by_fd (vol_fd, PEEK));
return NULL;
}
#endif /* WINDOWS */
while (is_retry == true)
{
is_retry = false;
nbytes_written = fileio_os_write (thread_p, vol_fd, write_p, nbytes, offset);
if (nbytes_written != nbytes)
{
if (errno == EINTR)
{
is_retry = true;
}
else if (errno == ENOSPC)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_WRITE_OUT_OF_SPACE, 2, page_id,
fileio_get_volume_label_by_fd (vol_fd, PEEK));
goto error;
}
else
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_WRITE, 2, page_id,
fileio_get_volume_label_by_fd (vol_fd, PEEK));
goto error;
}
}
}
if (io_page_p != NULL)
{
free_and_init (io_page_p);
}
fileio_compensate_flush (thread_p, vol_fd, 1);
perfmon_inc_stat (thread_p, PSTAT_FILE_NUM_IOWRITES);
return area_p;
error:
if (io_page_p != NULL)
{
free_and_init (io_page_p);
}
return NULL;
}
#endif
/*
* fileio_get_number_of_volume_pages () - Find the size of the volume in number of pages
* return: Num pages
* vol_fd(in): Volume descriptor
*/
DKNPAGES
fileio_get_number_of_volume_pages (int vol_fd, size_t page_size)
{
off_t offset;
offset = lseek (vol_fd, 0L, SEEK_END);
return (DKNPAGES) (offset / page_size);
}
/*
* fileio_get_number_of_partition_free_pages () - Find the number of free pages in the given
* OS disk partition
* return: number of free pages
* path(in): Path to disk partition
*
* Note: The number of pages is in the size of the database system not
* the size of the OS system.
*/
int
fileio_get_number_of_partition_free_pages (const char *path_p, size_t page_size)
{
#if defined(WINDOWS)
return (free_space (path_p, (int) IO_PAGESIZE));
#else /* WINDOWS */
int vol_fd;
INT64 npages_of_partition = -1;
#if defined(SOLARIS)
struct statvfs buf;
#else /* SOLARIS */
struct statfs buf;
#endif /* SOLARIS */
#if defined(SOLARIS)
if (statvfs (path_p, &buf) == -1)
#elif defined(AIX)
if (statfs ((char *) path_p, &buf) == -1)
#else /* AIX */
if (statfs (path_p, &buf) == -1)
#endif /* AIX */
{
if (errno == ENOENT
&& ((vol_fd = fileio_open (path_p, FILEIO_DISK_FORMAT_MODE, FILEIO_DISK_PROTECTION_MODE)) != NULL_VOLDES))
{
/* The given file did not exist. We create it for temporary consumption then it is removed */
npages_of_partition = fileio_get_number_of_partition_free_pages (path_p, page_size);
/* Close the file and remove it */
fileio_close (vol_fd);
(void) remove (path_p);
}
else
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, path_p);
}
}
else
{
const size_t f_avail_size = buf.f_bsize * buf.f_bavail;
npages_of_partition = f_avail_size / page_size;
if (npages_of_partition < 0 || npages_of_partition > INT_MAX)
{
npages_of_partition = INT_MAX;
}
}
if (npages_of_partition < 0)
{
return -1;
}
else
{
assert (npages_of_partition <= INT_MAX);
return (int) npages_of_partition;
}
#endif /* WINDOWS */
}
/*
* fileio_get_number_of_partition_free_pages () - document me!
*
* return : Number of free sectors
* path_p (in) : Path to disk partition
*/
DKNSECTS
fileio_get_number_of_partition_free_sectors (const char *path_p)
{
#if defined(WINDOWS)
return (DKNSECTS) free_space (path_p, IO_SECTORSIZE);
#else /* WINDOWS */
int vol_fd;
INT64 nsectors_of_partition = -1;
#if defined(SOLARIS)
struct statvfs buf;
#else /* SOLARIS */
struct statfs buf;
#endif /* SOLARIS */
#if defined(SOLARIS)
if (statvfs (path_p, &buf) == -1)
#elif defined(AIX)
if (statfs ((char *) path_p, &buf) == -1)
#else /* AIX */
if (statfs (path_p, &buf) == -1)
#endif /* AIX */
{
if (errno == ENOENT
&& ((vol_fd = fileio_open (path_p, FILEIO_DISK_FORMAT_MODE, FILEIO_DISK_PROTECTION_MODE)) != NULL_VOLDES))
{
/* The given file did not exist. We create it for temporary consumption then it is removed */
nsectors_of_partition = fileio_get_number_of_partition_free_sectors (path_p);
/* Close the file and remove it */
fileio_close (vol_fd);
(void) remove (path_p);
}
else
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, path_p);
}
}
else
{
const size_t f_avail_size = buf.f_bsize * buf.f_bavail;
nsectors_of_partition = f_avail_size / IO_SECTORSIZE;
if (nsectors_of_partition < 0 || nsectors_of_partition > INT_MAX)
{
nsectors_of_partition = INT_MAX;
}
}
if (nsectors_of_partition < 0)
{
return -1;
}
else
{
assert (nsectors_of_partition <= INT_MAX);
return (DKNSECTS) nsectors_of_partition;
}
#endif /* WINDOWS */
}
/*
* fileio_rename () - Rename the volume from "old_vlabel" to "new_vlabel"
* return: new_vlabel or NULL in case of error
* volid(in): Volume Identifier
* old_vlabel(in): Old volume label
* new_vlabel(in): New volume label
*/
const char *
fileio_rename (VOLID vol_id, const char *old_label_p, const char *new_label_p)
{
#if defined(CUBRID_DEBUG)
if (fileio_get_volume_descriptor (vol_id) != NULL_VOLDES)
{
er_log_debug (ARG_FILE_LINE, "io_rename: SYSTEM ERROR..The volume %s must be dismounted to rename a volume...");
return NULL;
}
#endif /* CUBRID_DEBUG */
if (os_rename_file (old_label_p, new_label_p) != NO_ERROR)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_RENAME_FAIL, 2, old_label_p, new_label_p);
return NULL;
}
return new_label_p;
}
/*
* fileio_is_volume_exist () - Find if a volume exist
* return: true/false
* vlabel(in): Volume label
*/
bool
fileio_is_volume_exist (const char *vol_label_p)
{
int vol_fd;
#if !defined(CS_MODE)
/* Is volume already mounted ? */
vol_fd = fileio_find_volume_descriptor_with_label (vol_label_p);
if (vol_fd != NULL_VOLDES)
{
return true;
}
#endif /* !CS_MODE */
/* Check the existance of the file by opening the file */
vol_fd = fileio_open (vol_label_p, O_RDONLY, 0);
if (vol_fd == NULL_VOLDES)
{
if (errno == ENOENT)
{
return false;
}
}
else
{
fileio_close (vol_fd);
}
return true;
}
/*
* fileio_is_volume_exist_and_file () - Find if a volume exist and is a regular file
* return: true/false
* vlabel(in): Volume label
*
* Note: This is to differentiate between directories, raw devices, and files.
*/
bool
fileio_is_volume_exist_and_file (const char *vol_label_p)
{
int vol_fd;
struct stat stbuf;
/* Is volume already mounted ? */
vol_fd = fileio_find_volume_descriptor_with_label (vol_label_p);
if (vol_fd != NULL_VOLDES)
{
return true;
}
if (stat (vol_label_p, &stbuf) != -1 && S_ISREG (stbuf.st_mode))
{
return true;
}
return false;
}
static char *
fileio_check_file_exist (char *name_p, char *new_guess_path_p, int check_size, int *max_name_size_p)
{
char *tmp_name_p;
int vol_fd = NULL_VOLDES;
tmp_name_p = name_p - (*max_name_size_p - check_size + 1);
*tmp_name_p = '\0';
vol_fd = fileio_open (new_guess_path_p, O_RDONLY, 0);
if (vol_fd == NULL_VOLDES)
{
vol_fd = fileio_open (new_guess_path_p, FILEIO_DISK_FORMAT_MODE, FILEIO_DISK_PROTECTION_MODE);
if (vol_fd == NULL_VOLDES && errno == ENAMETOOLONG)
{
*max_name_size_p = check_size + 1;
name_p = tmp_name_p;
}
else
{
if (vol_fd != NULL_VOLDES)
{
fileio_close (vol_fd);
(void) remove (new_guess_path_p);
}
*tmp_name_p = 'x';
}
}
else
{
*tmp_name_p = 'x';
if (vol_fd != NULL_VOLDES)
{
fileio_close (vol_fd);
}
}
return name_p;
}
static char *
fileio_check_file_is_same (char *name_p, char *new_guess_path_p, int check_size, int *max_name_size_p, struct stat *buf)
{
char *tmp_name_p;
tmp_name_p = name_p - (*max_name_size_p - check_size + 1);
*tmp_name_p = '\0';
if (stat (new_guess_path_p, &buf[1]) == 0 && buf[0].st_ino == buf[1].st_ino)
{
*max_name_size_p = check_size + 1;
name_p = tmp_name_p;
}
else
{
*tmp_name_p = 'x';
}
return name_p;
}
/*
* fileio_get_primitive_way_max () - Find the longest names of files and
* path names that can be given for
* the given file system (path) in a
* primitive way
* return: filename max
* path(in): Path to directory or file name
* filename_max(out): the longest name that could be given
* pathname_max(out): the longest path that could be given
*
* Note: This function should only be used when the values cannot be
* determine using pathconf.
*/
static int
fileio_get_primitive_way_max (const char *path_p, long int *file_name_max_p, long int *path_name_max_p)
{
static char last_guess_path[PATH_MAX] = { '\0' };
static int max_name_size = -1;
char new_guess_path[PATH_MAX];
char *name_p;
int check256, check14;
bool is_remove = false;
int vol_fd = NULL_VOLDES;
struct stat buf[2];
int i;
int success;
*file_name_max_p = NAME_MAX;
*path_name_max_p = PATH_MAX;
if (*file_name_max_p > *path_name_max_p)
{
*file_name_max_p = *path_name_max_p;
}
/* Verify the above compilation guesses */
strncpy_bufsize (new_guess_path, path_p);
name_p = strrchr (new_guess_path, '/');
#if defined(WINDOWS)
{
char *tmp_name = strrchr (new_guess_path, '\\');
if (name_p < tmp_name)
name_p = tmp_name;
}
#endif /* WINDOWS */
if (name_p != NULL)
{
*++name_p = '\0';
}
else
{
name_p = new_guess_path;
}
if (max_name_size != -1 && strcmp (last_guess_path, new_guess_path) == 0)
{
return *file_name_max_p = max_name_size;
}
for (max_name_size = 1, i = (int) strlen (new_guess_path) + 1;
max_name_size < *file_name_max_p && i < *path_name_max_p; max_name_size++, i++)
{
*name_p++ = 'x';
}
*name_p++ = '\0';
/* Start from the back until you find a file which is different. The assumption is that the files do not exist. */
check256 = 1;
check14 = 1;
while (max_name_size > 1)
{
vol_fd = fileio_open (new_guess_path, O_RDONLY, 0);
if (vol_fd != NULL_VOLDES)
{
/* The file already exist */
is_remove = false;
break;
}
else
{
/* The file did not exist. Create the file and at the end remove the file */
is_remove = true;
vol_fd = fileio_open (new_guess_path, FILEIO_DISK_FORMAT_MODE, FILEIO_DISK_PROTECTION_MODE);
if (vol_fd != NULL_VOLDES)
{
break;
}
if (errno != ENAMETOOLONG)
{
goto error;
}
/*
* Name truncation is not allowed. Most Unix systems accept
* filename of 256 or 14.
* Assume one of this for now
*/
if (max_name_size > 257 && check256 == 1)
{
check256 = 0;
name_p = fileio_check_file_exist (name_p, new_guess_path, 256, &max_name_size);
}
else if (max_name_size > 15 && check14 == 1)
{
check14 = 0;
name_p = fileio_check_file_exist (name_p, new_guess_path, 14, &max_name_size);
}
*name_p-- = '\0';
max_name_size--;
}
}
strncpy (last_guess_path, new_guess_path, PATH_MAX);
if (vol_fd != NULL_VOLDES)
{
fileio_close (vol_fd);
if (stat (new_guess_path, &buf[0]) == -1)
{
goto error;
}
}
else
{
goto error;
}
/*
* Most Unix system are either 256 or 14. Do a quick check to see if 15
* is the same than current value. If it is, set maxname to 15 and decrement
* name.
*/
check256 = 1;
check14 = 1;
for (; max_name_size > 1; max_name_size--)
{
*name_p-- = '\0';
if ((success = stat (new_guess_path, &buf[1])) == 0 && buf[0].st_ino == buf[1].st_ino)
{
/*
* Same file. Most Unix system allow either 256 or 14 for filenames.
* Perform a quick check to see if we can speed up the checking
* process
*/
if (max_name_size > 257 && check256 == 1)
{
check256 = 0;
name_p = fileio_check_file_is_same (name_p, new_guess_path, 256, &max_name_size, buf);
/* Check if the name with 257 is the same. If it is advance the to 256 */
}
else if (max_name_size > 15 && check14 == 1)
{
check14 = 0;
name_p = fileio_check_file_is_same (name_p, new_guess_path, 14, &max_name_size, buf);
}
}
else
{
if (success == 0)
{
continue;
}
else if (errno == ENOENT)
{
/* The file did not exist or the file is different. Therefore, previous maxname is the maximum name */
max_name_size++;
break;
}
goto error;
}
}
/* The length has been found */
if (is_remove == true)
{
(void) remove (last_guess_path);
}
name_p = strrchr (last_guess_path, '/');
#if defined(WINDOWS)
{
char *tmp_name = strrchr (last_guess_path, '\\');
if (name_p < tmp_name)
{
name_p = tmp_name;
}
}
#endif /* WINDOWS */
if (name_p != NULL)
{
*++name_p = '\0';
}
/* Plus 2 since we start with zero and we need to include null character */
max_name_size = max_name_size + 2;
return *file_name_max_p = max_name_size;
error:
if (is_remove == true)
{
(void) remove (last_guess_path);
}
max_name_size = -1;
*path_name_max_p = -1;
*file_name_max_p = -1;
return -1;
}
/*
* fileio_get_max_name () - Find the longest names of files and path
* names that can be given for the given file
* system (path)
* return: filename max
* path(in): Path to directory or file name
* filename_max(out): the longest name that could be given
* pathname_max(out): the longest path that could be given
*
* Note: The main goal of this function is to respect the limits that
* the database system is using at compile time (e.g., variables
* defined with PATH_MAX) and at run time. For example, if
* the constant FILENAME_MAX cannot be used to detect long names
* since this value at compilation time may be different from the
* value at execution time (e.g., at other installation). If we
* use the compiled value, it may be possible that we will be
* removing a file when a new one is created when truncation of
* filenames is allowed at the running file system.
* In addition, it is possible that such limits may differ across
* file systems, device boundaries. For example, Unix System V
* uses a maximum of 14 characters for file names, and Unix BSD
* uses 255. On this implementations, we are forced to use 14
* characters.
* The functions returns the minimum of the compilation and run
* time for both filename and pathname.
*/
int
fileio_get_max_name (const char *given_path_p, long int *file_name_max_p, long int *path_name_max_p)
{
char new_path[PATH_MAX];
const char *path_p;
char *name_p;
struct stat stbuf;
/* Errno need to be reset to find out if the values are not handle */
errno = 0;
path_p = given_path_p;
*file_name_max_p = pathconf ((char *) path_p, _PC_NAME_MAX);
*path_name_max_p = pathconf ((char *) path_p, _PC_PATH_MAX);
if ((*file_name_max_p < 0 || *path_name_max_p < 0) && (errno == ENOENT || errno == EINVAL))
{
/*
* The above values may not be accepted for that path. The path may be
* a file instead of a directory, try it with the directory since some
* implementations cannot answer the above question when the path is a
* file
*/
if (stat (path_p, &stbuf) != -1 && ((stbuf.st_mode & S_IFMT) != S_IFDIR))
{
/* Try it with the directory instead */
strncpy_bufsize (new_path, given_path_p);
name_p = strrchr (new_path, '/');
#if defined(WINDOWS)
{
char *tmp_name = strrchr (new_path, '\\');
if (name_p < tmp_name)
name_p = tmp_name;
}
#endif /* WINDOWS */
if (name_p != NULL)
{
*name_p = '\0';
}
path_p = new_path;
*file_name_max_p = pathconf ((char *) path_p, _PC_NAME_MAX);
*path_name_max_p = pathconf ((char *) path_p, _PC_PATH_MAX);
path_p = given_path_p;
}
}
if (*file_name_max_p < 0 || *path_name_max_p < 0)
{
/* If errno is zero, the values are indeterminate */
(void) fileio_get_primitive_way_max (path_p, file_name_max_p, path_name_max_p);
}
/* Make sure that we do not overpass compilation structures */
if (*file_name_max_p < 0 || *file_name_max_p > NAME_MAX)
{
*file_name_max_p = NAME_MAX;
}
if (*path_name_max_p < 0 || *path_name_max_p > PATH_MAX)
{
*path_name_max_p = PATH_MAX;
}
return *file_name_max_p;
}
/*
* fileio_get_base_file_name () - Find start of basename in given filename
* return: basename
* fullname(in): Fullname of file
*/
const char *
fileio_get_base_file_name (const char *full_name_p)
{
const char *no_path_name_p;
no_path_name_p = strrchr (full_name_p, PATH_SEPARATOR);
#if defined(WINDOWS)
{
const char *nn_tmp = strrchr (full_name_p, '/');
if (no_path_name_p < nn_tmp)
{
no_path_name_p = nn_tmp;
}
}
#endif /* WINDOWS */
if (no_path_name_p == NULL)
{
no_path_name_p = full_name_p;
}
else
{
no_path_name_p++; /* Skip to the name */
}
return no_path_name_p;
}
/*
* fileio_get_directory_path () - Find directory path of given file. That is copy all but the
* basename of filename
* return: path
* path(out): The path of the file
* fullname(in): Fullname of file
*/
char *
fileio_get_directory_path (char *path_p, const char *full_name_p)
{
const char *base_p;
size_t path_size;
base_p = fileio_get_base_file_name (full_name_p);
assert (base_p >= full_name_p);
if (base_p == full_name_p)
{
/* Same pointer, the file does not contain a path/directory portion. Use the current directory */
if (getcwd (path_p, PATH_MAX) == NULL)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_BO_CWD_FAIL, 0);
*path_p = '\0';
}
}
else
{
path_size = (size_t) (base_p - full_name_p - 1);
if (path_size > PATH_MAX)
{
path_size = PATH_MAX;
}
memcpy (path_p, full_name_p, path_size);
path_p[path_size] = '\0';
}
return path_p;
}
/*
* fileio_get_volume_max_suffix () -
* return:
*/
int
fileio_get_volume_max_suffix (void)
{
return FILEIO_MAX_SUFFIX_LENGTH;
}
/*
* fileio_make_volume_lock_name () - Build the name of volumes
* return: void
* vol_lockname(out):
* vol_fullname(in):
*
* Note: The caller must have enough space to store the name of the volume
* that is constructed(sprintf). It is recommended to have at least
* DB_MAX_PATH_LENGTH length.
*/
static void
fileio_make_volume_lock_name (char *vol_lock_name_p, const char *vol_full_name_p)
{
sprintf (vol_lock_name_p, "%s%s", vol_full_name_p, FILEIO_VOLLOCK_SUFFIX);
}
/*
* fileio_make_volume_info_name () - Build the name of volumes
* return: void
* volinfo_name(out):
* db_fullname(in):
*
* Note: The caller must have enough space to store the name of the volume
* that is constructed(sprintf). It is recommended to have at least
* DB_MAX_PATH_LENGTH length.
*/
void
fileio_make_volume_info_name (char *vol_info_name_p, const char *db_full_name_p)
{
sprintf (vol_info_name_p, "%s%s", db_full_name_p, FILEIO_VOLINFO_SUFFIX);
}
/*
* fileio_make_volume_ext_name () - Build the name of volumes
* return: void
* volext_fullname(out):
* ext_path(in):
* ext_name(in):
* volid(in):
*
* Note: The caller must have enough space to store the name of the volume
* that is constructed(sprintf). It is recommended to have at least
* DB_MAX_PATH_LENGTH length.
*/
void
fileio_make_volume_ext_name (char *vol_ext_full_name_p, const char *ext_path_p, const char *ext_name_p, VOLID vol_id)
{
sprintf (vol_ext_full_name_p, "%s%s%s%s%03d", ext_path_p, FILEIO_PATH_SEPARATOR (ext_path_p), ext_name_p,
FILEIO_VOLEXT_PREFIX, vol_id);
}
/*
* fileio_make_volume_ext_given_name () - Build the name of volumes
* return: void
* volext_fullname(out):
* ext_path(in):
* ext_name(in):
*
* Note: The caller must have enough space to store the name of the volume
* that is constructed(sprintf). It is recommended to have at least
* DB_MAX_PATH_LENGTH length.
*/
void
fileio_make_volume_ext_given_name (char *vol_ext_full_name_p, const char *ext_path_p, const char *ext_name_p)
{
sprintf (vol_ext_full_name_p, "%s%s%s", ext_path_p, FILEIO_PATH_SEPARATOR (ext_path_p), ext_name_p);
}
/*
* fileio_make_volume_temp_name () - Build the name of volumes
* return: void
* voltmp_fullname(out):
* tmp_path(in):
* tmp_name(in):
* volid(in):
*
* Note: The caller must have enough space to store the name of the volume
* that is constructed(sprintf). It is recommended to have at least
* DB_MAX_PATH_LENGTH length.
*/
void
fileio_make_volume_temp_name (char *vol_tmp_full_name_p, const char *tmp_path_p, const char *tmp_name_p, VOLID vol_id)
{
sprintf (vol_tmp_full_name_p, "%s%c%s%s%03d", tmp_path_p, PATH_SEPARATOR, tmp_name_p, FILEIO_VOLTMP_PREFIX, vol_id);
}
/*
* fileio_make_log_active_name () - Build the name of volumes
* return: void
* logactive_name(out):
* log_path(in):
* dbname(in):
*
* Note: The caller must have enough space to store the name of the volume
* that is constructed(sprintf). It is recommended to have at least
* DB_MAX_PATH_LENGTH length.
*/
void
fileio_make_log_active_name (char *log_active_name_p, const char *log_path_p, const char *db_name_p)
{
sprintf (log_active_name_p, "%s%s%s%s", log_path_p, FILEIO_PATH_SEPARATOR (log_path_p), db_name_p,
FILEIO_SUFFIX_LOGACTIVE);
}
/*
* fileio_make_temp_log_files_from_backup () - Build the name of volumes
* return: void
* logactive_name(out):
* level(in):
* active_name(in):
*
* Note: The caller must have enough space to store the name of the volume
* that is constructed(sprintf). It is recommended to have at least
* DB_MAX_PATH_LENGTH length.
*/
void
fileio_make_temp_log_files_from_backup (char *temp_log_name, VOLID to_volid, FILEIO_BACKUP_LEVEL level,
const char *base_log_name)
{
switch (to_volid)
{
case LOG_DBLOG_ACTIVE_VOLID:
sprintf (temp_log_name, "%s_%03d_tmp", base_log_name, level);
break;
case LOG_DBLOG_INFO_VOLID:
sprintf (temp_log_name, "%s_%03d_tmp", base_log_name, level);
break;
case LOG_DBLOG_ARCHIVE_VOLID:
sprintf (temp_log_name, "%s_%03d_tmp", base_log_name, level);
break;
default:
break;
}
}
/*
* fileio_make_log_archive_name () - Build the name of volumes
* return: void
* logarchive_name(out):
* log_path(in):
* dbname(in):
* arvnum(in):
*
* Note: The caller must have enough space to store the name of the volume
* that is constructed(sprintf). It is recommended to have at least
* DB_MAX_PATH_LENGTH length.
*/
void
fileio_make_log_archive_name (char *log_archive_name_p, const char *log_path_p, const char *db_name_p,
int archive_number)
{
sprintf (log_archive_name_p, "%s%s%s%s%03d", log_path_p, FILEIO_PATH_SEPARATOR (log_path_p), db_name_p,
FILEIO_SUFFIX_LOGARCHIVE, archive_number);
}
/*
* fileio_make_removed_log_archive_name () - Build the name of removed volumes
* return: void
* logarchive_name(out):
* log_path(in):
* dbname(in):
*
* Note: The caller must have enough space to store the name of the volume
* that is constructed(sprintf). It is recommended to have at least
* DB_MAX_PATH_LENGTH length.
*/
void
fileio_make_removed_log_archive_name (char *log_archive_name_p, const char *log_path_p, const char *db_name_p)
{
sprintf (log_archive_name_p, "%s%s%s%s.removed", log_path_p, FILEIO_PATH_SEPARATOR (log_path_p), db_name_p,
FILEIO_SUFFIX_LOGARCHIVE);
}
/*
* fileio_make_log_archive_temp_name () -
* return: void
* logarchive_name_p(out):
* log_path_p(in):
* db_name_p(in):
*
* Note:
*/
void
fileio_make_log_archive_temp_name (char *log_archive_temp_name_p, const char *log_path_p, const char *db_name_p)
{
const char *fmt_string_p;
fmt_string_p = "%s%s%s%s";
snprintf (log_archive_temp_name_p, PATH_MAX - 1, fmt_string_p, log_path_p, FILEIO_PATH_SEPARATOR (log_path_p),
db_name_p, FILEIO_SUFFIX_TMP_LOGARCHIVE);
}
/*
* fileio_make_log_info_name () - Build the name of volumes
* return: void
* loginfo_name(out):
* log_path(in):
* dbname(in):
*
* Note: The caller must have enough space to store the name of the volume
* that is constructed(sprintf). It is recommended to have at least
* DB_MAX_PATH_LENGTH length.
*/
void
fileio_make_log_info_name (char *log_info_name_p, const char *log_path_p, const char *db_name_p)
{
sprintf (log_info_name_p, "%s%s%s%s", log_path_p, FILEIO_PATH_SEPARATOR (log_path_p), db_name_p,
FILEIO_SUFFIX_LOGINFO);
}
/*
* fileio_make_backup_volume_info_name () - Build the name of volumes
* return: void
* backup_volinfo_name(out):
* backinfo_path(in):
* dbname(in):
*
* Note: The caller must have enough space to store the name of the volume
* that is constructed(sprintf). It is recommended to have at least
* DB_MAX_PATH_LENGTH length.
*/
void
fileio_make_backup_volume_info_name (char *backup_volinfo_name_p, const char *backup_info_path_p, const char *db_name_p)
{
sprintf (backup_volinfo_name_p, "%s%s%s%s", backup_info_path_p, FILEIO_PATH_SEPARATOR (backup_info_path_p), db_name_p,
FILEIO_SUFFIX_BACKUP_VOLINFO);
}
/*
* fileio_make_backup_name () - Build the name of volumes
* return: void
* backup_name(out):
* nopath_volname(in):
* backup_path(in):
* level(in):
* unit_num(in):
*
* Note: The caller must have enough space to store the name of the volume
* that is constructed(sprintf). It is recommended to have at least
* DB_MAX_PATH_LENGTH length.
*/
void
fileio_make_backup_name (char *backup_name_p, const char *no_path_vol_name_p, const char *backup_path_p,
FILEIO_BACKUP_LEVEL level, int unit_num)
{
if (unit_num >= 0)
{
sprintf (backup_name_p, "%s%c%s%s%dv%03d", backup_path_p, PATH_SEPARATOR, no_path_vol_name_p,
FILEIO_SUFFIX_BACKUP, level, unit_num);
}
else
{
/* without unit number, usually with FILEIO_NO_BACKUP_UNITS */
sprintf (backup_name_p, "%s%c%s%s%d", backup_path_p, PATH_SEPARATOR, no_path_vol_name_p, FILEIO_SUFFIX_BACKUP,
level);
}
}
/*
* fileio_make_dwb_name () - Build the name of DWB volume
* return: void
* dwb_name_p(out): the name of DWB volume
* dwb_path_p(in): double write buffer path
* dbname(in): database name
*
* Note: The caller must have enough space to store the name of the volume
* that is constructed(sprintf). It is recommended to have at least
* DB_MAX_PATH_LENGTH length.
*/
void
fileio_make_dwb_name (char *dwb_name_p, const char *dwb_path_p, const char *db_name_p)
{
sprintf (dwb_name_p, "%s%s%s%s", dwb_path_p, FILEIO_PATH_SEPARATOR (dwb_path_p), db_name_p, FILEIO_SUFFIX_DWB);
}
/*
* fileio_make_keys_name () - Build the name of KEYS file (for TDE Master Key)
* return: void
* keys_name_p(out): the name of KEYS file
* db_full_name_p(in): database full path
*
* Note: The caller must have enough space to store the name of the volume
* that is constructed(sprintf). It is recommended to have at least
* DB_MAX_PATH_LENGTH length.
*/
void
fileio_make_keys_name (char *keys_name_p, const char *db_full_name_p)
{
sprintf (keys_name_p, "%s%s", db_full_name_p, FILEIO_SUFFIX_KEYS);
}
/*
* fileio_make_keys_name_given_path () - Build the name of KEYS file (for TDE Master Key)
* return: void
* keys_name_p(out): the bname of KEYS file
* keys_path_p(in): the directory path of KEYS file
* db_name_p(in): database name
*
* Note: The caller must have enough space to store the name of the volume
* that is constructed(sprintf). It is recommended to have at least
* DB_MAX_PATH_LENGTH length.
*/
void
fileio_make_keys_name_given_path (char *keys_name_p, const char *keys_path_p, const char *db_name_p)
{
sprintf (keys_name_p, "%s%s%s%s", keys_path_p, FILEIO_PATH_SEPARATOR (keys_path_p), db_name_p, FILEIO_SUFFIX_KEYS);
}
#ifdef UNSTABLE_TDE_FOR_REPLICATION_LOG
/*
* fileio_make_ha_sock_name () - Build the name of HA socket name (for sharing TDE Data keys)
* return: void
* keys_name_p(out): the name of KEYS volume
* dbname(in): database name
*
* Note: The caller must have enough space to store the name of the volume
* that is constructed(sprintf). It is recommended to have at least
* DB_MAX_PATH_LENGTH length.
*/
void
fileio_make_ha_sock_name (char *sock_path_p, const char *base_path_p, const char *sock_name_p)
{
sprintf (sock_path_p, "%s%s%s", base_path_p, FILEIO_PATH_SEPARATOR (base_path_p), sock_name_p);
}
#endif /* UNSTABLE_TDE_FOR_REPLICATION_LOG */
/*
* fileio_cache () - Cache information related to a mounted volume
* return: vdes on success, NULL_VOLDES on failure
* volid(in): Permanent volume identifier
* vlabel(in): Name/label of the volume
* vdes(in): I/O volume descriptor
* lockf_type(in): Type of lock
*/
static int
fileio_cache (VOLID vol_id, const char *vol_label_p, int vol_fd, FILEIO_LOCKF_TYPE lockf_type)
{
bool is_permanent_volume;
FILEIO_VOLUME_INFO *vol_info_p;
FILEIO_SYSTEM_VOLUME_INFO *sys_vol_info_p;
int i, j, rv;
FILEIO_CHECK_AND_INITIALIZE_VOLUME_HEADER_CACHE (NULL_VOLDES);
if (vol_id > NULL_VOLID)
{
/* perm volume */
if (vol_id < fileio_Vol_info_header.next_temp_volid)
{
i = vol_id / FILEIO_VOLINFO_INCREMENT;
j = vol_id % FILEIO_VOLINFO_INCREMENT;
if (vol_id >= fileio_Vol_info_header.max_perm_vols
&& fileio_expand_permanent_volume_info (&fileio_Vol_info_header, vol_id) < 0)
{
return NULL_VOLDES;
}
is_permanent_volume = true;
}
else
{
/* volid is the next temp volume id */
i = (fileio_Vol_info_header.num_volinfo_array - 1 - (LOG_MAX_DBVOLID - vol_id) / FILEIO_VOLINFO_INCREMENT);
j = (FILEIO_VOLINFO_INCREMENT - 1 - (LOG_MAX_DBVOLID - vol_id) % FILEIO_VOLINFO_INCREMENT);
if (((LOG_MAX_DBVOLID - vol_id) >= fileio_Vol_info_header.max_temp_vols)
&& fileio_expand_temporary_volume_info (&fileio_Vol_info_header, vol_id) < 0)
{
return NULL_VOLDES;
}
is_permanent_volume = false;
}
vol_info_p = &fileio_Vol_info_header.volinfo[i][j];
vol_info_p->volid = vol_id;
vol_info_p->vdes = vol_fd;
vol_info_p->lockf_type = lockf_type;
strncpy (vol_info_p->vlabel, vol_label_p, PATH_MAX);
/* modify next volume id */
rv = pthread_mutex_lock (&fileio_Vol_info_header.mutex);
if (is_permanent_volume)
{
if (fileio_Vol_info_header.next_perm_volid <= vol_id)
{
fileio_Vol_info_header.next_perm_volid = vol_id + 1;
}
}
else
{
if (fileio_Vol_info_header.next_temp_volid >= vol_id)
{
fileio_Vol_info_header.next_temp_volid = vol_id - 1;
}
}
pthread_mutex_unlock (&fileio_Vol_info_header.mutex);
}
else
{
if (vol_id == LOG_DBDWB_VOLID)
{
/* Do not cache DWB. */
return vol_fd;
}
/* system volume */
rv = pthread_mutex_lock (&fileio_Sys_vol_info_header.mutex);
if (fileio_Sys_vol_info_header.anchor.vdes != NULL_VOLDES)
{
sys_vol_info_p = (FILEIO_SYSTEM_VOLUME_INFO *) malloc (sizeof (FILEIO_SYSTEM_VOLUME_INFO));
if (sys_vol_info_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
sizeof (FILEIO_SYSTEM_VOLUME_INFO));
vol_fd = NULL_VOLDES;
}
else
{
sys_vol_info_p->volid = vol_id;
sys_vol_info_p->vdes = vol_fd;
sys_vol_info_p->lockf_type = lockf_type;
strncpy_bufsize (sys_vol_info_p->vlabel, vol_label_p);
sys_vol_info_p->next = fileio_Sys_vol_info_header.anchor.next;
fileio_Sys_vol_info_header.anchor.next = sys_vol_info_p;
fileio_Sys_vol_info_header.num_vols++;
#if defined(WINDOWS)
pthread_mutex_init (&sys_vol_info_p->sysvol_mutex, NULL);
#endif /* WINDOWS */
}
}
else
{
sys_vol_info_p = &fileio_Sys_vol_info_header.anchor;
sys_vol_info_p->volid = vol_id;
sys_vol_info_p->vdes = vol_fd;
sys_vol_info_p->lockf_type = lockf_type;
sys_vol_info_p->next = NULL;
strncpy_bufsize (sys_vol_info_p->vlabel, vol_label_p);
#if defined(WINDOWS)
pthread_mutex_init (&sys_vol_info_p->sysvol_mutex, NULL);
#endif /* WINDOWS */
fileio_Sys_vol_info_header.num_vols++;
}
pthread_mutex_unlock (&fileio_Sys_vol_info_header.mutex);
}
return vol_fd;
}
/*
* fileio_decache () - Decache volume information. Used when the volume is
* dismounted
* return: void
* vdes(in): I/O Volume descriptor
*/
static void
fileio_decache (THREAD_ENTRY * thread_p, int vol_fd)
{
FILEIO_SYSTEM_VOLUME_INFO *sys_vol_info_p, *prev_sys_vol_info_p;
FILEIO_VOLUME_INFO *vol_info_p;
int vol_id, prev_vol;
int rv;
APPLY_ARG arg = { 0 };
rv = pthread_mutex_lock (&fileio_Sys_vol_info_header.mutex);
/* sys volume ? */
for ((sys_vol_info_p = &fileio_Sys_vol_info_header.anchor, prev_sys_vol_info_p = NULL);
(sys_vol_info_p != NULL && sys_vol_info_p->vdes != NULL_VOLDES);
prev_sys_vol_info_p = sys_vol_info_p, sys_vol_info_p = sys_vol_info_p->next)
{
if (sys_vol_info_p->vdes == vol_fd)
{
if (prev_sys_vol_info_p == NULL)
{
if (fileio_Sys_vol_info_header.anchor.next != NULL)
{
/* copy next volinfo to anchor. */
sys_vol_info_p = fileio_Sys_vol_info_header.anchor.next;
fileio_Sys_vol_info_header.anchor.volid = sys_vol_info_p->volid;
fileio_Sys_vol_info_header.anchor.vdes = sys_vol_info_p->vdes;
fileio_Sys_vol_info_header.anchor.lockf_type = sys_vol_info_p->lockf_type;
strncpy (fileio_Sys_vol_info_header.anchor.vlabel, sys_vol_info_p->vlabel, PATH_MAX);
fileio_Sys_vol_info_header.anchor.next = sys_vol_info_p->next;
#if defined(SERVER_MODE) && defined(WINDOWS)
pthread_mutex_destroy (&sys_vol_info_p->sysvol_mutex);
#endif /* WINDOWS */
free_and_init (sys_vol_info_p);
}
else
{
fileio_Sys_vol_info_header.anchor.volid = NULL_VOLID;
fileio_Sys_vol_info_header.anchor.vdes = NULL_VOLDES;
fileio_Sys_vol_info_header.anchor.lockf_type = FILEIO_NOT_LOCKF;
fileio_Sys_vol_info_header.anchor.vlabel[0] = '\0';
fileio_Sys_vol_info_header.anchor.next = NULL;
#if defined(SERVER_MODE) && defined(WINDOWS)
pthread_mutex_destroy (&fileio_Sys_vol_info_header.anchor.sysvol_mutex);
#endif /* WINDOWS */
}
}
else
{
prev_sys_vol_info_p->next = sys_vol_info_p->next;
#if defined(SERVER_MODE) && defined(WINDOWS)
pthread_mutex_destroy (&sys_vol_info_p->sysvol_mutex);
#endif /* WINDOWS */
free_and_init (sys_vol_info_p);
}
fileio_Sys_vol_info_header.num_vols--;
pthread_mutex_unlock (&fileio_Sys_vol_info_header.mutex);
return;
}
}
pthread_mutex_unlock (&fileio_Sys_vol_info_header.mutex);
arg.vdes = vol_fd;
vol_info_p = fileio_traverse_permanent_volume (thread_p, fileio_is_volume_descriptor_equal, &arg);
if (vol_info_p)
{
vol_id = vol_info_p->volid;
/* update next_perm_volid, if needed */
rv = pthread_mutex_lock (&fileio_Vol_info_header.mutex);
if (fileio_Vol_info_header.next_perm_volid == vol_id + 1)
{
fileio_Vol_info_header.next_perm_volid = fileio_find_previous_perm_volume (thread_p, vol_id) + 1;
}
pthread_mutex_unlock (&fileio_Vol_info_header.mutex);
vol_info_p->volid = NULL_VOLID;
vol_info_p->vdes = NULL_VOLDES;
vol_info_p->lockf_type = FILEIO_NOT_LOCKF;
vol_info_p->vlabel[0] = '\0';
#if defined(SERVER_MODE) && defined(WINDOWS)
pthread_mutex_destroy (&vol_info_p->vol_mutex);
#endif /* WINDOWS */
return;
}
arg.vdes = vol_fd;
vol_info_p = fileio_traverse_temporary_volume (thread_p, fileio_is_volume_descriptor_equal, &arg);
if (vol_info_p)
{
vol_id = vol_info_p->volid;
/* update next_temp_volid, if needed */
rv = pthread_mutex_lock (&fileio_Vol_info_header.mutex);
if (fileio_Vol_info_header.next_temp_volid == vol_id - 1)
{
prev_vol = fileio_find_previous_temp_volume (thread_p, vol_id);
/* if prev_vol is NULL_VOLID, this volume is last volume */
fileio_Vol_info_header.next_temp_volid = (prev_vol != NULL_VOLID) ? (prev_vol - 1) : (LOG_MAX_DBVOLID);
}
pthread_mutex_unlock (&fileio_Vol_info_header.mutex);
vol_info_p->volid = NULL_VOLID;
vol_info_p->vdes = NULL_VOLDES;
vol_info_p->lockf_type = FILEIO_NOT_LOCKF;
vol_info_p->vlabel[0] = '\0';
#if defined(SERVER_MODE) && defined(WINDOWS)
pthread_mutex_destroy (&vol_info_p->vol_mutex);
#endif /* WINDOWS */
return;
}
}
/*
* fileio_get_volume_label ()
* - Find the name of a mounted volume given its permanent volume identifier
* return: Volume label
* volid(in): Permanent volume identifier
*/
char *
fileio_get_volume_label (VOLID vol_id, bool is_peek)
{
FILEIO_VOLUME_INFO *vol_info_p;
FILEIO_SYSTEM_VOLUME_INFO *sys_vol_info_p;
char *vol_label_p = NULL;
int i, j, rv;
APPLY_ARG arg = { 0 };
FILEIO_CHECK_AND_INITIALIZE_VOLUME_HEADER_CACHE (NULL);
if (vol_id > NULL_VOLID)
{
/* perm volume */
if (vol_id < fileio_Vol_info_header.next_temp_volid)
{
if (vol_id >= fileio_Vol_info_header.max_perm_vols)
{
return NULL;
}
i = vol_id / FILEIO_VOLINFO_INCREMENT;
j = vol_id % FILEIO_VOLINFO_INCREMENT;
}
else
{
/* volid is the next temp volume id */
if ((LOG_MAX_DBVOLID - vol_id) >= fileio_Vol_info_header.max_temp_vols)
{
return NULL;
}
i = fileio_Vol_info_header.num_volinfo_array - 1 - (LOG_MAX_DBVOLID - vol_id) / FILEIO_VOLINFO_INCREMENT;
j = FILEIO_VOLINFO_INCREMENT - 1 - (LOG_MAX_DBVOLID - vol_id) % FILEIO_VOLINFO_INCREMENT;
}
vol_info_p = &fileio_Vol_info_header.volinfo[i][j];
vol_label_p = (char *) vol_info_p->vlabel;
}
else
{
/* system volume */
rv = pthread_mutex_lock (&fileio_Sys_vol_info_header.mutex);
arg.vol_id = vol_id;
sys_vol_info_p = fileio_find_system_volume (NULL, fileio_is_system_volume_id_equal, &arg);
if (sys_vol_info_p)
{
vol_label_p = (char *) sys_vol_info_p->vlabel;
}
pthread_mutex_unlock (&fileio_Sys_vol_info_header.mutex);
}
if (!is_peek)
{
char *ret = strdup (vol_label_p);
if (ret == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) strlen (vol_label_p));
}
return ret;
}
return vol_label_p;
}
/*
* fileio_get_volume_label_by_fd ()
* - Find the name of a mounted volume given its file descriptor
* return: Volume label
* vol_fd(in): volume descriptor
*/
char *
fileio_get_volume_label_by_fd (int vol_fd, bool is_peek)
{
return fileio_get_volume_label (fileio_get_volume_id (vol_fd), is_peek);
}
/*
* fileio_get_volume_id () - Find volume identifier of a mounted volume given its
* descriptor
* return: The volume identifier
* vdes(in): I/O volume descriptor
*/
static VOLID
fileio_get_volume_id (int vol_fd)
{
FILEIO_VOLUME_INFO *vol_info_p;
FILEIO_SYSTEM_VOLUME_INFO *sys_vol_info_p;
VOLID vol_id = NULL_VOLID;
int rv;
APPLY_ARG arg = { 0 };
FILEIO_CHECK_AND_INITIALIZE_VOLUME_HEADER_CACHE (NULL_VOLID);
/* sys volume ? */
rv = pthread_mutex_lock (&fileio_Sys_vol_info_header.mutex);
arg.vdes = vol_fd;
sys_vol_info_p = fileio_find_system_volume (NULL, fileio_is_system_volume_descriptor_equal, &arg);
if (sys_vol_info_p)
{
vol_id = sys_vol_info_p->volid;
pthread_mutex_unlock (&fileio_Sys_vol_info_header.mutex);
return vol_id;
}
pthread_mutex_unlock (&fileio_Sys_vol_info_header.mutex);
arg.vdes = vol_fd;
vol_info_p = fileio_traverse_permanent_volume (NULL, fileio_is_volume_descriptor_equal, &arg);
if (vol_info_p)
{
return vol_info_p->volid;
}
arg.vdes = vol_fd;
vol_info_p = fileio_traverse_temporary_volume (NULL, fileio_is_volume_descriptor_equal, &arg);
if (vol_info_p)
{
return vol_info_p->volid;
}
return vol_id;
}
static bool
fileio_is_volume_label_equal (THREAD_ENTRY * thread_p, FILEIO_VOLUME_INFO * vol_info_p, APPLY_ARG * arg)
{
return (util_compare_filepath (vol_info_p->vlabel, arg->vol_label) == 0);
}
/*
* fileio_find_volume_id_with_label () - Find the volume identifier given the volume label of
* a mounted volume
* return: The volume identifier
* vlabel(in): Volume Name/label
*/
VOLID
fileio_find_volume_id_with_label (THREAD_ENTRY * thread_p, const char *vol_label_p)
{
FILEIO_VOLUME_INFO *vol_info_p;
FILEIO_SYSTEM_VOLUME_INFO *sys_vol_info_p;
VOLID vol_id = NULL_VOLID;
int rv;
APPLY_ARG arg = { 0 };
FILEIO_CHECK_AND_INITIALIZE_VOLUME_HEADER_CACHE (NULL_VOLID);
rv = pthread_mutex_lock (&fileio_Sys_vol_info_header.mutex);
arg.vol_label = vol_label_p;
sys_vol_info_p = fileio_find_system_volume (thread_p, fileio_is_system_volume_label_equal, &arg);
if (sys_vol_info_p)
{
vol_id = sys_vol_info_p->volid;
pthread_mutex_unlock (&fileio_Sys_vol_info_header.mutex);
return vol_id;
}
pthread_mutex_unlock (&fileio_Sys_vol_info_header.mutex);
arg.vol_label = vol_label_p;
vol_info_p = fileio_traverse_permanent_volume (thread_p, fileio_is_volume_label_equal, &arg);
if (vol_info_p)
{
return vol_info_p->volid;
}
arg.vol_label = vol_label_p;
vol_info_p = fileio_traverse_temporary_volume (thread_p, fileio_is_volume_label_equal, &arg);
if (vol_info_p)
{
return vol_info_p->volid;
}
return vol_id;
}
bool
fileio_is_temp_volume (THREAD_ENTRY * thread_p, VOLID volid)
{
FILEIO_VOLUME_INFO *vol_info_p;
APPLY_ARG arg = { 0 };
if (volid == NULL_VOLID)
{
return false;
}
FILEIO_CHECK_AND_INITIALIZE_VOLUME_HEADER_CACHE (false);
arg.vol_id = volid;
vol_info_p = fileio_traverse_temporary_volume (thread_p, fileio_is_volume_id_equal, &arg);
if (vol_info_p)
{
assert (fileio_get_volume_descriptor (volid) != NULL_VOLDES);
return true;
}
return false;
}
/*
* fileio_is_permanent_volume_descriptor () - Check whether is permanent volume descriptor.
* return: I/O volume descriptor
* vol_fd(in): Volume descriptor to check.
*/
bool
fileio_is_permanent_volume_descriptor (THREAD_ENTRY * thread_p, int vol_fd)
{
FILEIO_VOLUME_INFO *vol_info_p;
APPLY_ARG arg = { 0 };
if (vol_fd == NULL_VOLDES)
{
return false;
}
FILEIO_CHECK_AND_INITIALIZE_VOLUME_HEADER_CACHE (false);
arg.vdes = vol_fd;
vol_info_p = fileio_traverse_permanent_volume (thread_p, fileio_is_volume_descriptor_equal, &arg);
if (vol_info_p)
{
return true;
}
return false;
}
VOLID
fileio_find_next_perm_volume (THREAD_ENTRY * thread_p, VOLID volid)
{
FILEIO_VOLUME_INFO *vol_info_p;
APPLY_ARG arg = { 0 };
if (volid == NULL_VOLID)
{
return NULL_VOLID;
}
FILEIO_CHECK_AND_INITIALIZE_VOLUME_HEADER_CACHE (NULL_VOLID);
arg.vol_id = volid;
vol_info_p = fileio_traverse_permanent_volume (thread_p, fileio_is_volume_id_gt, &arg);
if (vol_info_p)
{
assert (fileio_get_volume_descriptor (volid) != NULL_VOLDES);
return vol_info_p->volid;
}
return NULL_VOLID;
}
VOLID
fileio_find_previous_perm_volume (THREAD_ENTRY * thread_p, VOLID volid)
{
FILEIO_VOLUME_INFO *vol_info_p;
APPLY_ARG arg = { 0 };
if (volid == NULL_VOLID)
{
return NULL_VOLID;
}
FILEIO_CHECK_AND_INITIALIZE_VOLUME_HEADER_CACHE (NULL_VOLID);
arg.vol_id = volid;
vol_info_p = fileio_reverse_traverse_permanent_volume (thread_p, fileio_is_volume_id_lt, &arg);
if (vol_info_p)
{
assert (fileio_get_volume_descriptor (volid) != NULL_VOLDES);
return vol_info_p->volid;
}
return NULL_VOLID;
}
VOLID
fileio_find_previous_temp_volume (THREAD_ENTRY * thread_p, VOLID volid)
{
FILEIO_VOLUME_INFO *vol_info_p;
APPLY_ARG arg = { 0 };
if (volid == NULL_VOLID)
{
return NULL_VOLID;
}
FILEIO_CHECK_AND_INITIALIZE_VOLUME_HEADER_CACHE (NULL_VOLID);
arg.vol_id = volid;
vol_info_p = fileio_reverse_traverse_temporary_volume (thread_p, fileio_is_volume_id_gt, &arg);
if (vol_info_p)
{
assert (fileio_get_volume_descriptor (volid) != NULL_VOLDES);
return vol_info_p->volid;
}
return NULL_VOLID;
}
/*
* fileio_get_volume_descriptor () - Find the volume descriptor given the volume permanent
* identifier
* return: I/O volume descriptor
* volid(in): Permanent volume identifier
*/
int
fileio_get_volume_descriptor (VOLID vol_id)
{
FILEIO_VOLUME_INFO *vol_info_p;
FILEIO_SYSTEM_VOLUME_INFO *sys_vol_info_p;
int vol_fd = NULL_VOLDES;
int i, j, rv;
APPLY_ARG arg = { 0 };
FILEIO_CHECK_AND_INITIALIZE_VOLUME_HEADER_CACHE (NULL_VOLDES);
if (vol_id > NULL_VOLID)
{
/* perm volume */
if (vol_id < fileio_Vol_info_header.next_temp_volid)
{
if (vol_id >= fileio_Vol_info_header.max_perm_vols)
{
return NULL_VOLDES;
}
i = vol_id / FILEIO_VOLINFO_INCREMENT;
j = vol_id % FILEIO_VOLINFO_INCREMENT;
}
else
{
/* volid is the next temp volume id */
if ((LOG_MAX_DBVOLID - vol_id) >= fileio_Vol_info_header.max_temp_vols)
{
return NULL_VOLDES;
}
i = fileio_Vol_info_header.num_volinfo_array - 1 - (LOG_MAX_DBVOLID - vol_id) / FILEIO_VOLINFO_INCREMENT;
j = FILEIO_VOLINFO_INCREMENT - 1 - (LOG_MAX_DBVOLID - vol_id) % FILEIO_VOLINFO_INCREMENT;
}
vol_info_p = &fileio_Vol_info_header.volinfo[i][j];
vol_fd = vol_info_p->vdes;
}
else
{
rv = pthread_mutex_lock (&fileio_Sys_vol_info_header.mutex);
arg.vol_id = vol_id;
sys_vol_info_p = fileio_find_system_volume (NULL, fileio_is_system_volume_id_equal, &arg);
if (sys_vol_info_p)
{
vol_fd = sys_vol_info_p->vdes;
}
pthread_mutex_unlock (&fileio_Sys_vol_info_header.mutex);
}
return vol_fd;
}
/*
* fileio_find_volume_descriptor_with_label () - Find the volume descriptor given the volume label/name
* return: Volume Name/label
* vlabel(in): I/O volume descriptor
*/
int
fileio_find_volume_descriptor_with_label (const char *vol_label_p)
{
FILEIO_VOLUME_INFO *vol_info_p;
FILEIO_SYSTEM_VOLUME_INFO *sys_vol_info_p;
int vol_fd = NULL_VOLDES;
int rv;
APPLY_ARG arg = { 0 };
FILEIO_CHECK_AND_INITIALIZE_VOLUME_HEADER_CACHE (NULL_VOLDES);
rv = pthread_mutex_lock (&fileio_Sys_vol_info_header.mutex);
arg.vol_label = vol_label_p;
sys_vol_info_p = fileio_find_system_volume (NULL, fileio_is_system_volume_label_equal, &arg);
if (sys_vol_info_p)
{
vol_fd = sys_vol_info_p->vdes;
pthread_mutex_unlock (&fileio_Sys_vol_info_header.mutex);
return vol_fd;
}
pthread_mutex_unlock (&fileio_Sys_vol_info_header.mutex);
arg.vol_label = vol_label_p;
vol_info_p = fileio_traverse_permanent_volume (NULL, fileio_is_volume_label_equal, &arg);
if (vol_info_p)
{
return vol_info_p->vdes;
}
arg.vol_label = vol_label_p;
vol_info_p = fileio_traverse_temporary_volume (NULL, fileio_is_volume_label_equal, &arg);
if (vol_info_p)
{
return vol_info_p->vdes;
}
return vol_fd;
}
/*
* fileio_get_lockf_type () - Find the lock type applied to a mounted volume
* return: lockf_type
* vdes(in): I/O volume descriptor
*/
static FILEIO_LOCKF_TYPE
fileio_get_lockf_type (int vol_fd)
{
FILEIO_VOLUME_INFO *vol_info_p;
FILEIO_SYSTEM_VOLUME_INFO *sys_vol_info_p;
FILEIO_LOCKF_TYPE lockf_type = FILEIO_NOT_LOCKF;
int rv;
APPLY_ARG arg = { 0 };
FILEIO_CHECK_AND_INITIALIZE_VOLUME_HEADER_CACHE (FILEIO_NOT_LOCKF);
rv = pthread_mutex_lock (&fileio_Sys_vol_info_header.mutex);
arg.vdes = vol_fd;
sys_vol_info_p = fileio_find_system_volume (NULL, fileio_is_system_volume_descriptor_equal, &arg);
if (sys_vol_info_p)
{
lockf_type = sys_vol_info_p->lockf_type;
pthread_mutex_unlock (&fileio_Sys_vol_info_header.mutex);
return lockf_type;
}
pthread_mutex_unlock (&fileio_Sys_vol_info_header.mutex);
arg.vdes = vol_fd;
vol_info_p = fileio_traverse_permanent_volume (NULL, fileio_is_volume_descriptor_equal, &arg);
if (vol_info_p)
{
return vol_info_p->lockf_type;
}
arg.vdes = vol_fd;
vol_info_p = fileio_traverse_temporary_volume (NULL, fileio_is_volume_descriptor_equal, &arg);
if (vol_info_p)
{
return vol_info_p->lockf_type;
}
return lockf_type;
}
#if 0
/* currently, disable the following code. DO NOT DELETE ME NEED FUTURE OPTIMIZATION */
static void
fileio_determine_backup_buffer_size (FILEIO_BACKUP_SESSION * session_p, int buf_size)
{
int vol_size, max_buf_size;
vol_size = DB_INT32_MAX; /* 2G */
if ((int) prm_get_bigint_value (PRM_ID_IO_BACKUP_MAX_VOLUME_SIZE) > 0)
{
vol_size = MIN (vol_size, (int) prm_get_bigint_value (PRM_ID_IO_BACKUP_MAX_VOLUME_SIZE));
}
vol_size -= (FILEIO_BACKUP_HEADER_IO_SIZE + FILEIO_BACKUP_FILE_HEADER_PAGE_SIZE);
max_buf_size = buf_size * prm_get_integer_value (PRM_ID_IO_BACKUP_NBUFFERS);
while (max_buf_size > buf_size)
{
if (vol_size % max_buf_size < buf_size)
{
break; /* OK */
}
max_buf_size -= buf_size;
}
session_p->bkup.iosize = max_buf_size;
}
#endif
static int
fileio_initialize_backup_thread (FILEIO_BACKUP_SESSION * session_p, int num_threads)
{
FILEIO_THREAD_INFO *thread_info_p;
FILEIO_QUEUE *queue_p;
#if defined(SERVER_MODE)
int num_cpus;
int rv;
#endif /* SERVER_MODE */
thread_info_p = &session_p->read_thread_info;
queue_p = &thread_info_p->io_queue;
#if defined(SERVER_MODE)
rv = pthread_mutex_init (&thread_info_p->mtx, NULL);
if (rv != 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CSS_PTHREAD_MUTEX_INIT, 0);
return ER_CSS_PTHREAD_MUTEX_INIT;
}
rv = pthread_cond_init (&thread_info_p->rcv, NULL);
if (rv != 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CSS_PTHREAD_COND_INIT, 0);
return ER_CSS_PTHREAD_COND_INIT;
}
rv = pthread_cond_init (&thread_info_p->wcv, NULL);
if (rv != 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CSS_PTHREAD_COND_INIT, 0);
return ER_CSS_PTHREAD_COND_INIT;
}
/* get the number of CPUs */
// *INDENT-OFF*
num_cpus = cubthread::system_core_count ();
// *INDENT-ON*
/* check for the upper bound of threads */
if (num_threads == FILEIO_BACKUP_NUM_THREADS_AUTO)
{
thread_info_p->num_threads = num_cpus;
}
else
{
thread_info_p->num_threads = MIN (num_threads, num_cpus);
}
thread_info_p->num_threads = MIN (thread_info_p->num_threads, NUM_NORMAL_TRANS);
#else /* SERVER_MODE */
thread_info_p->num_threads = 1;
#endif /* SERVER_MODE */
#if defined(CUBRID_DEBUG)
fprintf (stdout, "PRM_CSS_MAX_CLIENTS = %d, tp->num_threads = %d\n", prm_get_integer_value (PRM_ID_CSS_MAX_CLIENTS),
thread_info_p->num_threads);
#endif /* CUBRID_DEBUG */
queue_p->size = 0;
queue_p->head = NULL;
queue_p->tail = NULL;
queue_p->free_list = NULL;
thread_info_p->initialized = true;
return NO_ERROR;
}
/*
* fileio_initialize_backup () - Initialize the backup session structure with the given
* information
* return: session or NULL
* db_fullname(in): Name of the database to backup
* backup_destination(in): Name of backup device (file or directory)
* session(out): The session array
* level(in): The presumed backup level
* verbose_file_path(in): verbose mode file path
* num_threads(in): number of threads
* sleep_msecs(in): sleep interval in msecs
*/
FILEIO_BACKUP_SESSION *
fileio_initialize_backup (const char *db_full_name_p, const char *backup_destination_p,
FILEIO_BACKUP_SESSION * session_p, FILEIO_BACKUP_LEVEL level, const char *verbose_file_path,
int num_threads, int sleep_msecs)
{
int vol_fd;
int size;
const char *db_nopath_name_p;
struct stat stbuf;
int buf_size;
int io_page_size;
const char *verbose_fp_mode;
/*
* First assume that backup device is a regular file or a raw device.
* Adjustments are made at a later point, if the backup_destination is
* a directory.
*/
strncpy_bufsize (session_p->bkup.name, backup_destination_p);
strncpy_bufsize (session_p->bkup.current_path, backup_destination_p);
session_p->bkup.vlabel = session_p->bkup.name;
session_p->bkup.vdes = NULL_VOLDES;
session_p->bkup.dtype = FILEIO_BACKUP_VOL_UNKNOWN;
session_p->dbfile.level = level;
session_p->bkup.buffer = NULL;
session_p->bkup.bkuphdr = NULL;
session_p->dbfile.area = NULL;
/* Now find out the type of backup_destination and the best page I/O for the backup. The accepted types are either
* file, directory, or raw device. */
while (stat (backup_destination_p, &stbuf) == -1)
{
/*
* Could not stat or backup_destination is a file or directory that does not exist.
* If the backup_destination does not exist, try to create it to make sure that we can write at this backup
* destination.
*/
vol_fd = fileio_open (backup_destination_p, FILEIO_DISK_FORMAT_MODE, FILEIO_DISK_PROTECTION_MODE);
if (vol_fd == NULL_VOLDES)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, backup_destination_p);
return NULL;
}
fileio_close (vol_fd);
continue;
}
if (S_ISDIR (stbuf.st_mode))
{
/*
* This is a DIRECTORY where the backup is going to be sent.
* The name of the backup file in this directory is labeled as databasename.bkLvNNN (Unix).
* In this case, we may destroy any previous backup in this directory.
*/
session_p->bkup.dtype = FILEIO_BACKUP_VOL_DIRECTORY;
db_nopath_name_p = fileio_get_base_file_name (db_full_name_p);
fileio_make_backup_name (session_p->bkup.name, db_nopath_name_p, backup_destination_p, level,
FILEIO_INITIAL_BACKUP_UNITS);
}
else if (S_ISREG (stbuf.st_mode))
{
/* Regular file. Remember the directory of this file, in case more volumes are needed. */
session_p->bkup.dtype = FILEIO_BACKUP_VOL_DIRECTORY;
fileio_get_directory_path (session_p->bkup.current_path, backup_destination_p);
}
else
{
/*
* ASSUME that everything else is a special file such as a FIFO file, a device(character or block special file)
* which is not named for I/O purposes. That is, the name of the device or regular file is used as the backup.
*/
session_p->bkup.dtype = FILEIO_BACKUP_VOL_DEVICE;
}
#if defined(WINDOWS)
buf_size = 4096;
#else /* WINDOWS */
buf_size = stbuf.st_blksize;
#endif /* WINDOWS */
/* User may override the default size by specifying a multiple of the natural block size for the device. */
session_p->bkup.iosize = buf_size * prm_get_integer_value (PRM_ID_IO_BACKUP_NBUFFERS);
#if 0
/* currently, disable the following code. DO NOT DELETE ME NEED FUTURE OPTIMIZATION */
fileio_determine_backup_buffer_size (session_p, buf_size);
#endif
if (prm_get_bigint_value (PRM_ID_IO_BACKUP_MAX_VOLUME_SIZE) > 0
&& ((UINT64) session_p->bkup.iosize >=
MIN (prm_get_bigint_value (PRM_ID_IO_BACKUP_MAX_VOLUME_SIZE), DB_INT32_MAX)))
{
er_log_debug (ARG_FILE_LINE,
"Backup block buffer size %ld must be less "
"than backup volume size %ld, resetting buffer size to %d\n", session_p->bkup.iosize,
prm_get_bigint_value (PRM_ID_IO_BACKUP_MAX_VOLUME_SIZE), buf_size);
session_p->bkup.iosize = MIN (buf_size, DB_INT32_MAX);
}
#if defined(CUBRID_DEBUG)
/* These print statements are candidates for part of a "verbose" option to backupdb. */
fprintf (stdout, "NATURAL BUFFER SIZE %ld (%d IO buffer blocks)\n", session_p->bkup.iosize,
session_p->bkup.iosize / buf_size);
fprintf (stdout, "BACKUP_MAX_VOLUME_SIZE = %ld\n", prm_get_bigint_value (PRM_ID_IO_BACKUP_MAX_VOLUME_SIZE));
#endif /* CUBRID_DEBUG */
/*
* Initialize backup device related information.
*
* Make sure it is large enough to hold various headers and pages.
* Beware that upon restore, both the backup buffer size and the
* database io pagesize may be different.
*/
io_page_size = IO_PAGESIZE;
if (session_p->dbfile.level == FILEIO_BACKUP_FULL_LEVEL)
{
io_page_size *= FILEIO_FULL_LEVEL_EXP;
}
size = MAX (io_page_size + FILEIO_BACKUP_PAGE_OVERHEAD, FILEIO_BACKUP_FILE_HEADER_PAGE_SIZE);
session_p->bkup.buffer = (char *) malloc (session_p->bkup.iosize);
if (session_p->bkup.buffer == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, session_p->bkup.iosize);
goto error;
}
session_p->dbfile.area = (FILEIO_BACKUP_PAGE *) malloc (size);
if (session_p->dbfile.area == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) size);
goto error;
}
session_p->bkup.bkuphdr = (FILEIO_BACKUP_HEADER *) malloc (FILEIO_BACKUP_HEADER_IO_SIZE);
if (session_p->bkup.bkuphdr == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, FILEIO_BACKUP_HEADER_IO_SIZE);
goto error;
}
session_p->bkup.ptr = session_p->bkup.buffer;
session_p->bkup.count = 0;
session_p->bkup.voltotalio = 0;
session_p->bkup.alltotalio = 0;
session_p->bkup.bkuphdr->unit_num = FILEIO_INITIAL_BACKUP_UNITS;
session_p->bkup.bkuphdr->level = level;
session_p->bkup.bkuphdr->bkup_iosize = session_p->bkup.iosize;
session_p->bkup.bkuphdr->bk_hdr_version = FILEIO_BACKUP_CURRENT_HEADER_VERSION;
session_p->bkup.bkuphdr->start_time = 0;
session_p->bkup.bkuphdr->end_time = -1;
memset (session_p->bkup.bkuphdr->db_prec_bkvolname, 0, sizeof (session_p->bkup.bkuphdr->db_prec_bkvolname));
memset (session_p->bkup.bkuphdr->db_next_bkvolname, 0, sizeof (session_p->bkup.bkuphdr->db_next_bkvolname));
session_p->bkup.bkuphdr->zip_method = FILEIO_ZIP_NONE_METHOD;
session_p->bkup.bkuphdr->zip_level = FILEIO_ZIP_NONE_LEVEL;
/* Initialize database file related information */
LSA_SET_NULL (&session_p->dbfile.lsa);
session_p->dbfile.vlabel = NULL;
session_p->dbfile.volid = NULL_VOLID;
session_p->dbfile.vdes = NULL_VOLDES;
session_p->dbfile.nbytes = -1;
FILEIO_SET_BACKUP_PAGE_ID (session_p->dbfile.area, NULL_PAGEID, io_page_size);
#if defined(CUBRID_DEBUG)
fprintf (stdout, "fileio_initialize_backup: %d\t%d,\t%d\n",
((FILEIO_BACKUP_PAGE *) (session_p->dbfile.area))->iopageid,
*(PAGEID *) (((char *) (session_p->dbfile.area)) + offsetof (FILEIO_BACKUP_PAGE, iopage) + io_page_size),
io_page_size);
#endif
if (fileio_initialize_backup_thread (session_p, num_threads) != NO_ERROR)
{
goto error;
}
if (verbose_file_path && *verbose_file_path)
{
if (session_p->type == FILEIO_BACKUP_WRITE && level == 0)
{
verbose_fp_mode = "w";
}
else
{
verbose_fp_mode = "a";
}
session_p->verbose_fp = fopen (verbose_file_path, verbose_fp_mode);
if (session_p->verbose_fp == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_CANNOT_OPEN_VERBOSE_FILE, 1, verbose_file_path);
goto error;
}
setbuf (session_p->verbose_fp, NULL);
}
else
{
session_p->verbose_fp = NULL;
}
session_p->sleep_msecs = sleep_msecs;
return session_p;
error:
if (session_p->bkup.buffer != NULL)
{
free_and_init (session_p->bkup.buffer);
}
if (session_p->dbfile.area != NULL)
{
free_and_init (session_p->dbfile.area);
}
if (session_p->bkup.bkuphdr != NULL)
{
free_and_init (session_p->bkup.bkuphdr);
}
if (session_p->verbose_fp != NULL)
{
fclose (session_p->verbose_fp);
session_p->verbose_fp = NULL;
}
return NULL;
}
/*
* fileio_finalize_backup_thread() -
* return: void
*
* session_p(in/out):
* zip_method(in):
*/
static void
fileio_finalize_backup_thread (FILEIO_BACKUP_SESSION * session_p, FILEIO_ZIP_METHOD zip_method)
{
FILEIO_THREAD_INFO *tp;
FILEIO_QUEUE *qp;
FILEIO_NODE *node, *node_next;
#if defined(SERVER_MODE)
int rv;
#endif /* SERVER_MODE */
tp = &session_p->read_thread_info;
qp = &tp->io_queue;
if (tp->initialized == false)
{
return;
}
#if defined(SERVER_MODE)
rv = pthread_mutex_destroy (&tp->mtx);
if (rv != 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CSS_PTHREAD_MUTEX_DESTROY, 0);
}
rv = pthread_cond_destroy (&tp->rcv);
if (rv != 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CSS_PTHREAD_COND_DESTROY, 0);
}
rv = pthread_cond_destroy (&tp->wcv);
if (rv != 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CSS_PTHREAD_COND_DESTROY, 0);
}
#endif /* SERVER_MODE */
while (qp->size > 0)
{
node = fileio_delete_queue_head (qp);
(void) fileio_free_node (qp, node);
}
for (node = qp->free_list; node; node = node_next)
{
node_next = node->next;
switch (zip_method)
{
case FILEIO_ZIP_LZ4_METHOD:
if (node->zip_info != NULL)
{
free_and_init (node->zip_info);
}
break;
case FILEIO_ZIP_LZO1X_METHOD:
break;
case FILEIO_ZIP_ZLIB_METHOD:
break;
default:
break;
}
if (node->area != NULL)
{
free_and_init (node->area);
}
if (node != NULL)
{
free_and_init (node);
}
}
qp->free_list = NULL;
tp->initialized = false;
}
/*
* fileio_abort_backup () - The backup session is aborted
* return: void
* session(in/out): The session array
* does_unformat_bk(in): set to TRUE to delete backup volumes we know about
*
* Note: The currently created backup file can be removed if desired. This
* routine is called in for normal cleanup as well as to handle
* exceptions.
*/
void
fileio_abort_backup (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session_p, bool does_unformat_bk)
{
FILEIO_BACKUP_HEADER *backup_header_p = session_p->bkup.bkuphdr;
FILEIO_ZIP_METHOD zip_method;
zip_method = backup_header_p ? backup_header_p->zip_method : FILEIO_ZIP_NONE_METHOD;
/* Remove the currently created backup */
if (session_p->bkup.vdes != NULL_VOLDES)
{
if (session_p->bkup.dtype == FILEIO_BACKUP_VOL_DIRECTORY)
{
if (session_p->type == FILEIO_BACKUP_READ)
{
/* access backup device for read */
fileio_dismount_without_fsync (thread_p, session_p->bkup.vdes);
}
else
{
/* access backup device for write */
fileio_dismount (thread_p, session_p->bkup.vdes);
}
}
else
{
fileio_dismount_without_fsync (thread_p, session_p->bkup.vdes);
}
}
/* Destroy the current backup volumes */
if (does_unformat_bk)
{
/* Remove current backup volume */
if (session_p->bkup.dtype == FILEIO_BACKUP_VOL_DIRECTORY
&& fileio_is_volume_exist_and_file (session_p->bkup.vlabel))
{
fileio_unformat (thread_p, session_p->bkup.vlabel);
}
/* Remove backup volumes previous to this one */
if (session_p->bkup.bkuphdr)
{
fileio_remove_all_backup (thread_p, session_p->bkup.bkuphdr->level);
}
}
if (session_p->verbose_fp)
{
fclose (session_p->verbose_fp);
session_p->verbose_fp = NULL;
}
/* Deallocate memory space */
if (session_p->bkup.buffer != NULL)
{
free_and_init (session_p->bkup.buffer);
}
if (session_p->dbfile.area != NULL)
{
free_and_init (session_p->dbfile.area);
}
if (session_p->bkup.bkuphdr != NULL)
{
free_and_init (session_p->bkup.bkuphdr);
}
fileio_finalize_backup_info (FILEIO_FIRST_BACKUP_VOL_INFO);
LSA_SET_NULL (&session_p->dbfile.lsa);
session_p->dbfile.level = FILEIO_BACKUP_UNDEFINED_LEVEL;
session_p->dbfile.vdes = NULL_VOLDES;
session_p->dbfile.volid = NULL_VOLID;
session_p->dbfile.vlabel = NULL;
session_p->dbfile.nbytes = -1;
session_p->dbfile.area = NULL;
session_p->bkup.vdes = NULL_VOLDES;
session_p->bkup.vlabel = NULL;
session_p->bkup.iosize = -1;
session_p->bkup.count = 0;
session_p->bkup.voltotalio = 0;
session_p->bkup.alltotalio = 0;
session_p->bkup.buffer = session_p->bkup.ptr = NULL;
session_p->bkup.bkuphdr = NULL;
fileio_finalize_backup_thread (session_p, zip_method);
}
/*
* fileio_start_backup () - Start a backup session
* return: session or NULL
* db_fullname(in): Name of the database to backup
* db_creation(in): Creation time of database
* backup_level(in): Backup level
* backup_start_lsa(in): start lsa for backup
* backup_chkpt_lsa(in): checkpoint lsa for backup
* all_levels_info(in): previous backup info per level
* session(in/out): The session array
* zip_method(in): compression method
* zip_level(in): compression evel
*
* Note: Note that fileio_initialize_backup must have already been invoked on the
* session.
*/
FILEIO_BACKUP_SESSION *
fileio_start_backup (THREAD_ENTRY * thread_p, const char *db_full_name_p, INT64 * db_creation_time_p,
FILEIO_BACKUP_LEVEL backup_level, LOG_LSA * backup_start_lsa_p, LOG_LSA * backup_checkpoint_lsa_p,
FILEIO_BACKUP_RECORD_INFO * all_levels_info_p, FILEIO_BACKUP_SESSION * session_p,
FILEIO_ZIP_METHOD zip_method, FILEIO_ZIP_LEVEL zip_level)
{
FILEIO_BACKUP_HEADER *backup_header_p;
int i;
/* Complete the session array initialization and create/open the backup destination device. */
LSA_COPY (&session_p->dbfile.lsa, backup_start_lsa_p);
session_p->bkup.vdes =
fileio_create_backup_volume (thread_p, db_full_name_p, session_p->bkup.vlabel, LOG_DBCOPY_VOLID, false, false,
(session_p->dbfile.level == FILEIO_BACKUP_FULL_LEVEL)
? FILEIO_BACKUP_MINIMUM_NUM_PAGES_FULL_LEVEL : FILEIO_BACKUP_MINIMUM_NUM_PAGES);
if (session_p->bkup.vdes == NULL_VOLDES)
{
goto error;
}
/* Remember name of new backup volume */
if (fileio_add_volume_to_backup_info (session_p->bkup.name, session_p->dbfile.level,
session_p->bkup.bkuphdr->unit_num, FILEIO_FIRST_BACKUP_VOL_INFO) != NO_ERROR)
{
goto error;
}
/* Write the description/header of the backup to the backup device */
backup_header_p = session_p->bkup.bkuphdr;
backup_header_p->iopageid = FILEIO_BACKUP_START_PAGE_ID;
strncpy (backup_header_p->magic, CUBRID_MAGIC_DATABASE_BACKUP, CUBRID_MAGIC_MAX_LENGTH);
strncpy_bufsize (backup_header_p->db_release, rel_release_string ());
strncpy_bufsize (backup_header_p->db_fullname, db_full_name_p);
backup_header_p->db_creation = *db_creation_time_p;
backup_header_p->db_iopagesize = IO_PAGESIZE;
backup_header_p->db_compatibility = rel_disk_compatible ();
backup_header_p->level = backup_level;
LSA_COPY (&backup_header_p->start_lsa, backup_start_lsa_p);
LSA_COPY (&backup_header_p->chkpt_lsa, backup_checkpoint_lsa_p);
for (i = FILEIO_BACKUP_FULL_LEVEL; i < FILEIO_BACKUP_UNDEFINED_LEVEL; i++)
{
if (all_levels_info_p)
{
if (i == FILEIO_BACKUP_FULL_LEVEL)
{
LSA_SET_NULL (&backup_header_p->previnfo[i].lsa);
}
else
{
LSA_COPY (&backup_header_p->previnfo[i].lsa, &all_levels_info_p[i].lsa);
}
backup_header_p->previnfo[i].at_time = all_levels_info_p[i].at_time;
}
else
{
LSA_SET_NULL (&backup_header_p->previnfo[i].lsa);
backup_header_p->previnfo[i].at_time = 0;
}
}
backup_header_p->start_time = time (NULL);
backup_header_p->unit_num = FILEIO_INITIAL_BACKUP_UNITS;
backup_header_p->bkpagesize = backup_header_p->db_iopagesize;
if (backup_level == FILEIO_BACKUP_FULL_LEVEL)
{
backup_header_p->bkpagesize *= FILEIO_FULL_LEVEL_EXP;
}
backup_header_p->zip_method = zip_method;
backup_header_p->zip_level = zip_level;
if (backup_header_p->zip_method == FILEIO_ZIP_LZ4_METHOD
&& FILEIO_DBVOLS_IO_PAGE_SIZE (backup_header_p) > LZ4_MAX_INPUT_SIZE)
{
goto error;
}
/* Now write this information to the backup volume. */
if (fileio_write_backup_header (session_p) != NO_ERROR)
{
goto error;
}
return session_p;
error:
fileio_abort_backup (thread_p, session_p, true);
return NULL;
}
/*
* fileio_write_backup_end_time_to_header () - Write the end time of backup
* to backup volume header
* return: error status
* session(in): backup session
* end_time: the end time of backup
*/
static int
fileio_write_backup_end_time_to_header (FILEIO_BACKUP_SESSION * session_p, INT64 end_time)
{
const char *first_bkvol_name;
int vdes, nbytes;
first_bkvol_name =
fileio_get_backup_info_volume_name (session_p->dbfile.level, FILEIO_INITIAL_BACKUP_UNITS,
FILEIO_FIRST_BACKUP_VOL_INFO);
if (first_bkvol_name == NULL)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, "(backup volume name is null)");
return ER_IO_MOUNT_FAIL;
}
if (strncmp (first_bkvol_name, session_p->bkup.vlabel, PATH_MAX) == 0)
{
session_p->bkup.bkuphdr->end_time = end_time;
lseek (session_p->bkup.vdes, 0, SEEK_SET);
fileio_write_backup_header (session_p);
}
else
{
vdes = fileio_open (first_bkvol_name, O_RDWR, 0);
if (vdes == NULL_VOLDES)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, first_bkvol_name);
return ER_IO_MOUNT_FAIL;
}
lseek (vdes, offsetof (FILEIO_BACKUP_HEADER, end_time), SEEK_SET);
nbytes = write (vdes, (char *) &end_time, sizeof (INT64));
if (nbytes != sizeof (INT64))
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_WRITE, 2, 1, first_bkvol_name);
fileio_close (vdes);
return ER_IO_WRITE;
}
fileio_close (vdes);
}
return NO_ERROR;
}
/*
* fileio_write_backup_end_time_to_last_page () - Write the end time of backup
* to the last page of backup
* return: void
* session(in): backup session
* end_time: the end time of backup
*/
static void
fileio_write_backup_end_time_to_last_page (FILEIO_BACKUP_SESSION * session_p, INT64 end_time)
{
char *write_to;
write_to = ((char *) &(session_p->dbfile.area->iopageid)) + offsetof (FILEIO_BACKUP_PAGE, iopage);
memcpy (write_to, (char *) &end_time, sizeof (INT64));
}
/*
* fileio_read_backup_end_time_from_last_page () - Read the end time of backup
* from the last page of backup
* return: void
* session(in): backup session
*/
static void
fileio_read_backup_end_time_from_last_page (FILEIO_BACKUP_SESSION * session_p)
{
char *read_from;
read_from = ((char *) &(session_p->dbfile.area->iopageid)) + offsetof (FILEIO_BACKUP_PAGE, iopage);
memcpy ((char *) &(session_p->bkup.bkuphdr->end_time), read_from, sizeof (INT64));
}
#if defined (SERVER_MODE) || defined (SA_MODE)
/*
* fileio_finish_backup () - Finish the backup session successfully
* return: session or NULL
* session(in/out): The session array
*/
FILEIO_BACKUP_SESSION *
fileio_finish_backup (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session_p)
{
int nbytes;
char *msg_area = NULL;
char io_time_val[CTIME_MAX];
INT64 end_time;
end_time = (INT64) time (NULL);
/*
* Indicate end of backup and flush any buffered data.
* Note that only the end of backup marker is written,
* so callers of io_restore_read must check for the appropriate
* end of backup condition.
*/
session_p->dbfile.area->iopageid = FILEIO_BACKUP_END_PAGE_ID;
nbytes = offsetof (FILEIO_BACKUP_PAGE, iopage);
if (session_p->bkup.dtype == FILEIO_BACKUP_VOL_DEVICE)
{
fileio_write_backup_end_time_to_last_page (session_p, end_time);
nbytes += sizeof (INT64);
}
if (fileio_write_backup (thread_p, session_p, nbytes) != NO_ERROR)
{
return NULL;
}
if (session_p->bkup.count > 0)
{
#if defined(CUBRID_DEBUG)
fprintf (stdout, "io_backup_end: iosize = %ld, count = %ld, voltotalio = %ld : EOF JUNK\n",
session_p->bkup.iosize, session_p->bkup.count, session_p->bkup.voltotalio);
#endif /* CUBRID_DEBUG */
/*
* We must add some junk at the end of the buffered area since some
* backup devices (e.g., Fixed-length I/O tape devices such as
* 1/4" cartridge tape devices), requires number of bytes to write
* to be a multiple of the physical record size (io_size).
*/
nbytes = CAST_BUFLEN (session_p->bkup.iosize - session_p->bkup.count);
memset (session_p->bkup.ptr, '\0', nbytes);
session_p->bkup.count = session_p->bkup.iosize;
/* Flush any buffered information */
if (fileio_flush_backup (thread_p, session_p) != NO_ERROR)
{
return NULL;
}
}
/*
* Now, make sure that all the information is physically written to
* the backup device. That is, make sure that nobody (e.g., backup
* device controller or OS) is caching data.
*/
if (session_p->bkup.dtype == FILEIO_BACKUP_VOL_DIRECTORY)
{
if (fileio_write_backup_end_time_to_header (session_p, end_time) != NO_ERROR)
{
return NULL;
}
if (fileio_synchronize (thread_p, session_p->bkup.vdes, session_p->bkup.name, true) != session_p->bkup.vdes)
{
return NULL;
}
}
/* Tell user that current backup volume just completed */
#if defined(SERVER_MODE) && !defined(WINDOWS)
if (asprintf (&msg_area, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_BACKUP_LABEL_INFO),
session_p->bkup.bkuphdr->level, session_p->bkup.bkuphdr->unit_num,
fileio_get_base_file_name (session_p->bkup.bkuphdr->db_fullname),
fileio_ctime (&session_p->bkup.bkuphdr->start_time, io_time_val)) < 0)
#else /* SERVER_MODE && !WINDOWS */
if (asprintf (&msg_area, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_BACKUP_LABEL_INFO),
session_p->bkup.bkuphdr->level, session_p->bkup.bkuphdr->unit_num,
fileio_get_base_file_name (session_p->bkup.bkuphdr->db_fullname),
fileio_ctime (&session_p->bkup.bkuphdr->start_time, io_time_val)) < 0)
#endif /* SERVER_MODE && !WINDOWS */
{
/* Note: we do not know the exact malloc size that failed */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) FILEIO_MAX_USER_RESPONSE_SIZE);
return NULL;
}
else
{
(void) fileio_request_user_response (thread_p, FILEIO_PROMPT_DISPLAY_ONLY, msg_area, NULL, NULL, -1, -1, NULL,
-1);
/* Note: Not free_and_init */
free (msg_area);
}
/*
* -------------------------------------------------------------------------
* WORKAROUND: Preventing Restore of Post-Backup Transactions
* -------------------------------------------------------------------------
*
* Problem:
* The restore process ('restoredb -d backuptime') compares the backup end time
* (stored in the backup volume header) with each transaction’s commit timestamp
* (LOG_COMMIT / LOG_ABORT).
*
* Issue:
* Since time(NULL) does not guarantee monotonic increase, the commit time
* obtained after backup completion can be equal to or earlier than the backup
* end time. In such cases, point-in-time restore may include transactions that
* were executed after the backup, which should have been excluded.
*
* Workaround:
* Enforce a 1-second delay after setting the backup end time so that any
* subsequent transaction obtains a strictly later commit timestamp.
*
* Limitation:
* This workaround resolves timing overlaps for transactions executed
* immediately after a backup in the same session, but cannot fully separate
* concurrent transactions across sessions due to second-level timestamp limits.
*
* TODO (Permanent Fix):
* - Use millisecond-level precision in LOG_COMMIT / LOG_ABORT timestamps to
* accurately separate concurrent transactions.
* -------------------------------------------------------------------------
*/
do
{
thread_sleep (1000);
}
while (end_time >= time (NULL));
return session_p;
}
#endif /* SERVER_MODE || SA_MODE */
/*
* fileio_remove_all_backup () - REMOVE ALL BACKUP VOLUMES
* return: void
* start_level(in): the starting level to remove
*
* Note: Initialize the backup session structure with the given information.
* Remove backup. Cleanup backup. This routine deletes all backups of
* a higher level as well. Furthermore, this routine assumes that the
* bkvinf cache has already been read in from the bkvinf file.
*/
void
fileio_remove_all_backup (THREAD_ENTRY * thread_p, int start_level)
{
int level;
int unit_num;
const char *vol_name_p;
level = start_level;
if (level >= FILEIO_BACKUP_UNDEFINED_LEVEL)
{
return;
}
if (level < FILEIO_BACKUP_FULL_LEVEL)
{
level = FILEIO_BACKUP_FULL_LEVEL;
}
do
{
unit_num = FILEIO_INITIAL_BACKUP_UNITS;
while (true)
{
vol_name_p =
fileio_get_backup_info_volume_name ((FILEIO_BACKUP_LEVEL) level, unit_num++, FILEIO_FIRST_BACKUP_VOL_INFO);
if (vol_name_p == NULL)
{
break;
}
if (fileio_is_volume_exist_and_file (vol_name_p))
{
fileio_unformat (thread_p, vol_name_p);
}
}
level++;
}
while (level < FILEIO_BACKUP_UNDEFINED_LEVEL);
/* Remove all names just deleted from memory */
fileio_clear_backup_info_level (start_level, false, FILEIO_FIRST_BACKUP_VOL_INFO);
}
/*
* fileio_allocate_node () -
* return:
* qp(in):
* backup_hdr(in):
*/
static FILEIO_NODE *
fileio_allocate_node (FILEIO_QUEUE * queue_p, FILEIO_BACKUP_HEADER * backup_header_p)
{
FILEIO_NODE *node_p;
int size;
int zip_info_size, buf_size;
if (queue_p->free_list) /* re-use already alloced nodes */
{
node_p = queue_p->free_list;
queue_p->free_list = node_p->next; /* cut-off */
return node_p;
}
/* at here, need to alloc */
node_p = (FILEIO_NODE *) malloc (sizeof (FILEIO_NODE));
if (node_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (FILEIO_NODE));
goto exit_on_error;
}
node_p->area = NULL;
node_p->zip_info = NULL;
size = FILEIO_DBVOLS_IO_PAGE_SIZE (backup_header_p);
node_p->area = (FILEIO_BACKUP_PAGE *) malloc (size);
if (node_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) size);
goto exit_on_error;
}
switch (backup_header_p->zip_method)
{
case FILEIO_ZIP_LZ4_METHOD:
assert (size <= LZ4_MAX_INPUT_SIZE);
// *INDENT-OFF*
buf_size = cubcompress::bound<cubcompress::LZ4> (size);
// *INDENT-ON*
zip_info_size = offsetof (FILEIO_ZIP_INFO, zip_page) + sizeof (int) + buf_size;
node_p->zip_info = (FILEIO_ZIP_INFO *) malloc (zip_info_size);
if (node_p->zip_info == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, zip_info_size);
goto exit_on_error;
}
node_p->zip_info->buf_size = buf_size;
break;
case FILEIO_ZIP_LZO1X_METHOD:
break;
case FILEIO_ZIP_ZLIB_METHOD:
break;
default:
break;
}
exit_on_end:
return node_p;
exit_on_error:
if (node_p)
{
if (node_p->zip_info)
{
free_and_init (node_p->zip_info);
}
if (node_p->area)
{
free_and_init (node_p->area);
}
free_and_init (node_p);
}
node_p = NULL;
goto exit_on_end;
}
/*
* fileio_free_node () -
* return:
* qp(in):
* node(in):
*/
static FILEIO_NODE *
fileio_free_node (FILEIO_QUEUE * queue_p, FILEIO_NODE * node_p)
{
if (node_p)
{
node_p->prev = node_p->next = NULL;
node_p->next = queue_p->free_list; /* add to free list */
queue_p->free_list = node_p;
}
return node_p;
}
/*
* fileio_append_queue () -
* return:
* qp(in):
* node(in):
*/
#if defined(SERVER_MODE)
static FILEIO_NODE *
fileio_append_queue (FILEIO_QUEUE * queue_p, FILEIO_NODE * node_p)
{
if (node_p)
{
node_p->prev = node_p->next = NULL;
node_p->next = queue_p->tail; /* add to tail */
if (queue_p->tail)
{
queue_p->tail->prev = node_p;
}
queue_p->tail = node_p;
if (queue_p->head == NULL)
{
/* the first */
queue_p->head = node_p;
}
queue_p->size++;
}
return node_p;
}
#endif /* SERVER_MODE */
/*
* fileio_delete_queue_head () -
* return:
* qp(in):
*/
static FILEIO_NODE *
fileio_delete_queue_head (FILEIO_QUEUE * queue_p)
{
FILEIO_NODE *node;
node = queue_p->head;
if (node)
{
if (node == queue_p->tail) /* only one node */
{
queue_p->tail = NULL;
}
else
{
node->prev->next = NULL; /* cut-off */
}
queue_p->head = node->prev;
queue_p->size--;
}
return node;
}
/*
* fileio_compress_backup_node () -
* return:
* node(in):
* backup_hdr(in):
*/
static int
fileio_compress_backup_node (FILEIO_NODE * node_p, FILEIO_BACKUP_HEADER * backup_header_p)
{
int error = NO_ERROR, local_buf_len;
FILEIO_ZIP_PAGE *zip_page;
if (!node_p || !node_p->zip_info || !backup_header_p)
{
goto exit_on_error;
}
assert (node_p->nread >= 0);
zip_page = &node_p->zip_info->zip_page;
switch (backup_header_p->zip_method)
{
case FILEIO_ZIP_LZ4_METHOD:
/* The alternative is compress faster - best speed, but, require more memory alloc */
// *INDENT-OFF*
local_buf_len =
cubcompress::compress<cubcompress::LZ4> ((char *) node_p->area, (int) node_p->nread, zip_page->buf,
node_p->zip_info->buf_size);
// *INDENT-ON*
if (local_buf_len <= 0)
{
/* best reduction */
error = ER_IO_LZ4_COMPRESS_FAIL;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 4, backup_header_p->zip_method,
fileio_get_zip_method_string (backup_header_p->zip_method), backup_header_p->zip_level,
fileio_get_zip_level_string (backup_header_p->zip_level));
#if defined(CUBRID_DEBUG)
fprintf (stdout,
"internal error - compression failed: node->pageid = %d, node->nread = %d, "
"buf_len = %d, buf_size = %d\n",
node_p->pageid, node_p->nread, local_buf_len, node_p->zip_info->buf_size);
#endif /* CUBRID_DEBUG */
goto exit_on_error;
}
assert (local_buf_len < node_p->zip_info->buf_size);
if ((ssize_t) local_buf_len < node_p->nread)
{
/* already write compressed block */
zip_page->buf_len = local_buf_len;
}
else
{
/* not compressible - write uncompressed block */
zip_page->buf_len = (int) node_p->nread;
memcpy (zip_page->buf, node_p->area, node_p->nread);
}
break;
case FILEIO_ZIP_LZO1X_METHOD:
error = ER_LOG_DBBACKUP_FAIL;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 4, backup_header_p->zip_method,
fileio_get_zip_method_string (backup_header_p->zip_method), backup_header_p->zip_level,
fileio_get_zip_level_string (backup_header_p->zip_level));
goto exit_on_error;
case FILEIO_ZIP_ZLIB_METHOD:
break;
default:
break;
}
exit_on_end:
return error;
exit_on_error:
if (error == NO_ERROR)
{
error = ER_FAILED;
}
goto exit_on_end;
}
/*
* fileio_write_backup_node () -
* return:
* thread_p(in):
* session_p(in/out):
* node_p(in):
* backup_header_p(in):
*/
static int
fileio_write_backup_node (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session_p, FILEIO_NODE * node_p,
FILEIO_BACKUP_HEADER * backup_header_p)
{
int error = NO_ERROR;
if (!session_p || !node_p || !backup_header_p)
{
goto exit_on_error;
}
switch (backup_header_p->zip_method)
{
case FILEIO_ZIP_LZ4_METHOD:
/* Skip allocated block size inside of FILEIO_ZIP_PAGE */
session_p->dbfile.area = (FILEIO_BACKUP_PAGE *) & node_p->zip_info->zip_page;
node_p->nread = sizeof (int) + node_p->zip_info->zip_page.buf_len;
break;
case FILEIO_ZIP_LZO1X_METHOD:
error = ER_LOG_DBBACKUP_FAIL;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 4, backup_header_p->zip_method,
fileio_get_zip_method_string (backup_header_p->zip_method), backup_header_p->zip_level,
fileio_get_zip_level_string (backup_header_p->zip_level));
goto exit_on_error;
case FILEIO_ZIP_ZLIB_METHOD:
break;
default:
session_p->dbfile.area = node_p->area;
break;
}
if (fileio_write_backup (thread_p, session_p, node_p->nread) != NO_ERROR)
{
goto exit_on_error;
}
exit_on_end:
return error;
exit_on_error:
if (error == NO_ERROR)
{
error = ER_FAILED;
}
goto exit_on_end;
}
/*
* fileio_read_backup_volume () -
* return:
* session(in/out):
*/
#if defined(SERVER_MODE)
static void
fileio_read_backup_volume (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session_p)
{
FILEIO_THREAD_INFO *thread_info_p;
FILEIO_QUEUE *queue_p;
FILEIO_NODE *node_p = NULL;
int rv;
bool need_unlock = false;
FILEIO_BACKUP_HEADER *backup_header_p;
FILEIO_BACKUP_PAGE *save_area_p;
if (thread_p == NULL)
{
thread_p = thread_get_thread_entry_info ();
if (thread_p == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return;
}
}
if (!session_p)
{
return;
}
thread_info_p = &session_p->read_thread_info;
queue_p = &thread_info_p->io_queue;
thread_p->tran_index = thread_info_p->tran_index;
#if defined(CUBRID_DEBUG)
fprintf (stdout, "start io_backup_volume_read, session = %p\n", session_p);
#endif /* CUBRID_DEBUG */
backup_header_p = session_p->bkup.bkuphdr;
node_p = NULL; /* init */
while (1)
{
rv = pthread_mutex_lock (&thread_info_p->mtx);
while (thread_info_p->io_type == FILEIO_WRITE)
{
pthread_cond_wait (&thread_info_p->rcv, &thread_info_p->mtx);
}
if (thread_info_p->io_type == FILEIO_ERROR_INTERRUPT)
{
need_unlock = true;
node_p = NULL;
goto exit_on_error;
}
/* get one page from queue head and do write */
if (node_p)
{
node_p->writeable = true;
thread_info_p->io_type = FILEIO_WRITE;
pthread_cond_signal (&thread_info_p->wcv); /* wake up write thread */
while (thread_info_p->io_type == FILEIO_WRITE)
{
pthread_cond_wait (&thread_info_p->rcv, &thread_info_p->mtx);
}
if (thread_info_p->io_type == FILEIO_ERROR_INTERRUPT)
{
need_unlock = true;
node_p = NULL;
goto exit_on_error;
}
}
/* check EOF */
if (thread_info_p->pageid >= thread_info_p->from_npages)
{
thread_info_p->end_r_threads++;
if (thread_info_p->end_r_threads >= thread_info_p->act_r_threads)
{
thread_info_p->io_type = FILEIO_WRITE;
pthread_cond_signal (&thread_info_p->wcv); /* wake up write thread */
}
pthread_mutex_unlock (&thread_info_p->mtx);
break;
}
/* alloc queue node */
node_p = fileio_allocate_node (queue_p, backup_header_p);
if (node_p == NULL)
{
thread_info_p->io_type = FILEIO_ERROR_INTERRUPT;
need_unlock = true;
goto exit_on_error;
}
/* read one page from Disk sequentially */
save_area_p = session_p->dbfile.area; /* save link */
session_p->dbfile.area = node_p->area;
node_p->pageid = thread_info_p->pageid;
node_p->writeable = false; /* init */
node_p->nread = fileio_read_backup (thread_p, session_p, node_p->pageid);
session_p->dbfile.area = save_area_p; /* restore link */
if (node_p->nread == -1)
{
thread_info_p->io_type = FILEIO_ERROR_INTERRUPT;
need_unlock = true;
goto exit_on_error;
}
else if (node_p->nread == 0)
{
/* This could be an error since we estimated more pages. End of file/volume. */
thread_info_p->io_type = FILEIO_ERROR_INTERRUPT;
need_unlock = true;
goto exit_on_error;
}
/* Have to allow other threads to run and check for interrupts from the user (i.e. Ctrl-C ) */
if ((thread_info_p->pageid % FILEIO_CHECK_FOR_INTERRUPT_INTERVAL) == 0
&& pgbuf_is_log_check_for_interrupts (thread_p) == true)
{
#if defined(CUBRID_DEBUG)
fprintf (stdout, "io_backup_volume_read interrupt\n");
#endif /* CUBRID_DEBUG */
thread_info_p->io_type = FILEIO_ERROR_INTERRUPT;
need_unlock = true;
goto exit_on_error;
}
/*
* Do we need to backup this page ?
* In other words, has it been changed since either the previous backup
* of this level or a lower level.
*/
if (thread_info_p->only_updated_pages == false || LSA_ISNULL (&session_p->dbfile.lsa)
|| LSA_LT (&session_p->dbfile.lsa, &node_p->area->iopage.prv.lsa))
{
/* Backup the content of this page along with its page identifier add alloced node to the queue */
(void) fileio_append_queue (queue_p, node_p);
}
else
{
/* free node */
(void) fileio_free_node (queue_p, node_p);
node_p = NULL;
}
#if defined(CUBRID_DEBUG)
fprintf (stdout, "read_thread from_npages = %d, pageid = %d\n", thread_info_p->from_npages,
thread_info_p->pageid);
#endif /* CUBRID_DEBUG */
thread_info_p->pageid++;
pthread_mutex_unlock (&thread_info_p->mtx);
if (node_p)
{
node_p->nread += FILEIO_BACKUP_PAGE_OVERHEAD;
FILEIO_SET_BACKUP_PAGE_ID_COPY (node_p->area, node_p->pageid, backup_header_p->bkpagesize);
#if defined(CUBRID_DEBUG)
fprintf (stdout, "fileio_read_backup_volume: %d\t%d,\t%d\n",
((FILEIO_BACKUP_PAGE *) (node_p->area))->iopageid,
*(PAGEID *) (((char *) (node_p->area)) + offsetof (FILEIO_BACKUP_PAGE, iopage) +
backup_header_p->bkpagesize), backup_header_p->bkpagesize);
#endif
if (backup_header_p->zip_method != FILEIO_ZIP_NONE_METHOD
&& fileio_compress_backup_node (node_p, backup_header_p) != NO_ERROR)
{
thread_info_p->io_type = FILEIO_ERROR_INTERRUPT;
need_unlock = false;
node_p = NULL;
goto exit_on_error;
}
}
}
exit_on_end:
#if defined(CUBRID_DEBUG)
fprintf (stdout, "end io_backup_volume_read\n");
#endif /* CUBRID_DEBUG */
return;
exit_on_error:
/* set error info */
if (thread_info_p->errid == NO_ERROR)
{
assert (er_errid () != NO_ERROR);
thread_info_p->errid = er_errid ();
}
thread_info_p->end_r_threads++;
if (thread_info_p->end_r_threads >= thread_info_p->act_r_threads)
{
pthread_cond_signal (&thread_info_p->wcv); /* wake up write thread */
}
if (need_unlock)
{
pthread_mutex_unlock (&thread_info_p->mtx);
}
if (node_p != NULL)
{
(void) fileio_free_node (queue_p, node_p);
}
goto exit_on_end;
}
/*
* fileio_write_backup_volume () -
* return:
* session(in/out):
*/
static FILEIO_TYPE
fileio_write_backup_volume (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session_p)
{
FILEIO_THREAD_INFO *thread_info_p;
FILEIO_QUEUE *queue_p;
FILEIO_NODE *node_p;
int rv;
bool need_unlock = false;
FILEIO_BACKUP_HEADER *backup_header_p;
FILEIO_BACKUP_PAGE *save_area_p;
if (!session_p)
{
return FILEIO_WRITE;
}
thread_info_p = &session_p->read_thread_info;
queue_p = &thread_info_p->io_queue;
#if defined(CUBRID_DEBUG)
fprintf (stdout, "start io_backup_volume_write\n");
#endif /* CUBRID_DEBUG */
backup_header_p = session_p->bkup.bkuphdr;
rv = pthread_mutex_lock (&thread_info_p->mtx);
while (1)
{
while (thread_info_p->io_type == FILEIO_READ)
{
pthread_cond_wait (&thread_info_p->wcv, &thread_info_p->mtx);
}
if (thread_info_p->io_type == FILEIO_ERROR_INTERRUPT)
{
need_unlock = true;
goto exit_on_error;
}
/* do write */
while (queue_p->head && queue_p->head->writeable == true)
{
/* delete the head node of the queue */
node_p = fileio_delete_queue_head (queue_p);
if (node_p == NULL)
{
thread_info_p->io_type = FILEIO_ERROR_INTERRUPT;
}
else
{
save_area_p = session_p->dbfile.area; /* save link */
rv = fileio_write_backup_node (thread_p, session_p, node_p, backup_header_p);
if (rv != NO_ERROR)
{
thread_info_p->io_type = FILEIO_ERROR_INTERRUPT;
}
session_p->dbfile.area = save_area_p; /* restore link */
#if defined(CUBRID_DEBUG)
fprintf (stdout, "write_thread node->pageid = %d, node->nread = %d\n", node_p->pageid, node_p->nread);
#endif /* CUBRID_DEBUG */
if (session_p->verbose_fp && thread_info_p->from_npages >= 25
&& node_p->pageid >= thread_info_p->check_npages)
{
fprintf (session_p->verbose_fp, "#");
thread_info_p->check_ratio++;
thread_info_p->check_npages =
(int) (((float) thread_info_p->from_npages / 25.0) * thread_info_p->check_ratio);
}
/* free node */
(void) fileio_free_node (queue_p, node_p);
}
if (thread_info_p->io_type == FILEIO_ERROR_INTERRUPT)
{
need_unlock = true;
goto exit_on_error;
}
}
thread_info_p->io_type = FILEIO_READ; /* reset */
/* check EOF */
if (thread_info_p->end_r_threads >= thread_info_p->act_r_threads)
{
/* only write thread alive */
pthread_mutex_unlock (&thread_info_p->mtx);
break;
}
pthread_cond_broadcast (&thread_info_p->rcv); /* wake up all read threads */
}
#if defined(CUBRID_DEBUG)
fprintf (stdout, "end io_backup_volume_write\n");
#endif /* CUBRID_DEBUG */
exit_on_end:
return thread_info_p->io_type;
exit_on_error:
/* set error info */
if (er_errid () == NO_ERROR)
{
switch (thread_info_p->errid)
{
case ER_INTERRUPTED:
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_INTERRUPTED, 0);
break;
default: /* give up to handle this case */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_LOG_DBBACKUP_FAIL, 1,
fileio_get_base_file_name (backup_header_p->db_fullname));
break;
}
}
if (thread_info_p->end_r_threads >= thread_info_p->act_r_threads)
{
/* only write thread alive an error (i.e, INTERRUPT) was broken out, so terminate the all threads. But, I am the
* last one, and all the readers are terminated */
pthread_mutex_unlock (&thread_info_p->mtx);
goto exit_on_end;
}
/* wake up all read threads and wait for all killed */
pthread_cond_broadcast (&thread_info_p->rcv);
pthread_cond_wait (&thread_info_p->wcv, &thread_info_p->mtx);
pthread_mutex_unlock (&thread_info_p->mtx);
goto exit_on_end;
}
// *INDENT-OFF*
static void
fileio_read_backup_volume_execute (cubthread::entry &thread_ref, FILEIO_BACKUP_SESSION *back_session)
{
fileio_read_backup_volume (&thread_ref, back_session);
}
// *INDENT-ON*
static int
fileio_start_backup_thread (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session_p,
FILEIO_THREAD_INFO * thread_info_p, int from_npages, bool is_only_updated_pages,
int check_ratio, int check_npages, FILEIO_QUEUE * queue_p)
{
CSS_CONN_ENTRY *conn_p;
int i;
/* Initialize global MT variables */
thread_info_p->end_r_threads = 0;
thread_info_p->pageid = 0;
thread_info_p->from_npages = from_npages;
thread_info_p->io_type = FILEIO_READ;
thread_info_p->errid = NO_ERROR;
thread_info_p->only_updated_pages = is_only_updated_pages;
thread_info_p->check_ratio = check_ratio;
thread_info_p->check_npages = check_npages;
thread_info_p->tran_index = LOG_FIND_THREAD_TRAN_INDEX (thread_p);
/* start read threads */
conn_p = css_get_current_conn_entry ();
for (i = 1; i <= thread_info_p->act_r_threads; i++)
{
// *INDENT-OFF
auto exec_f = std::bind (fileio_read_backup_volume_execute, std::placeholders::_1, session_p);
logpb_push_backup_read_task (new cubthread::entry_callable_task (exec_f));
// *INDENT-ON
}
/* work as write thread */
(void) fileio_write_backup_volume (thread_p, session_p);
/* at here, finished all read threads check error, interrupt */
if (thread_info_p->io_type == FILEIO_ERROR_INTERRUPT || queue_p->size != 0)
{
return ER_FAILED;
}
return NO_ERROR;
}
#endif /* SERVER_MODE */
#if !defined(CS_MODE)
/*
* fileio_backup_volume () - Include the given database volume/file as part of
* the backup
* return:
* session(in/out): The session array
* from_vlabel(in): Name of the database volume/file to include
* from_volid(in): Identifier of the database volume/file to include
* last_page(in): stop backing up this volume after this page
* only_updated_pages(in): If we are backing up the database with a specific
* backup level. We may opt to backup only the
* updated pages since previous backup.
*
* Note: Information about the database volume/file is recorded, so it
* can be recreated (e.g., name and space).
* If this is an incremental backup, only pages that have been
* updated since the previous backup are backed up, unless a
* specific request is given to backup all pages.
* Last_page can shorten the number of pages saved (i.e. for
* temp volumes, we do not need to backup the entire volume).
*
* 1) The pages are backed up as they are currently stored on disk,
* that is, we do not use the page buffer pool for this operation
* since we do not want to disturbe the normal access patern of
* clients in the page buffer pool.
* 2) We open the file/volume instead of using the actual vdes, so
* that we avoid a bunch of lseeks.
*/
int
fileio_backup_volume (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session_p, const char *from_vol_label_p,
VOLID from_vol_id, PAGEID last_page, bool is_only_updated_pages)
{
struct stat from_stbuf;
int from_npages, npages;
int page_id;
int nread;
FILEIO_BACKUP_FILE_HEADER *file_header_p;
int check_ratio = 0;
int check_npages = 0;
FILEIO_THREAD_INFO *thread_info_p;
FILEIO_QUEUE *queue_p = NULL;
FILEIO_BACKUP_PAGE *save_area_p;
FILEIO_NODE *node_p = NULL;
FILEIO_BACKUP_HEADER *backup_header_p;
int rv;
bool is_need_vol_closed;
#if (defined(WINDOWS) || !defined(SERVER_MODE))
off_t saved_act_log_fp = (off_t) - 1;
#endif /* WINDOWS || !SERVER_MODE */
/*
* Backup the pages as they are stored on disk (i.e., don't care if they
* are stored on the page buffer pool any longer). We do not use the page
* buffer pool since we do not want to remove important pages that are
* used by clients.
* We also open the file/volume instead of using the one currently
* available since we do not want to be doing a lot of seeks.
* Remember that we can be preempted.
*/
session_p->dbfile.vlabel = from_vol_label_p;
session_p->dbfile.volid = from_vol_id;
session_p->dbfile.vdes = NULL_VOLDES;
is_need_vol_closed = false;
if (from_vol_id == LOG_DBLOG_ACTIVE_VOLID)
{
session_p->dbfile.vdes = fileio_get_volume_descriptor (LOG_DBLOG_ACTIVE_VOLID);
#if (defined(WINDOWS) || !defined(SERVER_MODE))
if (session_p->dbfile.vdes != NULL_VOLDES)
{
/* save current file pointer */
saved_act_log_fp = lseek (session_p->dbfile.vdes, (off_t) 0, SEEK_CUR);
/* reset file pointer */
if (saved_act_log_fp == (off_t) - 1)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_READ, 1, session_p->dbfile.vlabel);
goto error;
}
if (lseek (session_p->dbfile.vdes, (off_t) 0, SEEK_SET) == (off_t) - 1)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_READ, 1, session_p->dbfile.vlabel);
goto error;
}
}
#endif /* WINDOWS || !SERVER_MODE */
}
if (session_p->dbfile.vdes == NULL_VOLDES)
{
session_p->dbfile.vdes = fileio_open (session_p->dbfile.vlabel, O_RDONLY, 0);
if (session_p->dbfile.vdes == NULL_VOLDES)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, session_p->dbfile.vlabel);
goto error;
}
is_need_vol_closed = true;
}
if (fstat (session_p->dbfile.vdes, &from_stbuf) == -1)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, session_p->dbfile.vlabel);
goto error;
}
if (S_ISREG (from_stbuf.st_mode))
{
/* regular file */
session_p->dbfile.nbytes = from_stbuf.st_size;
}
else
{
/* raw device??? */
session_p->dbfile.nbytes = (INT64) xdisk_get_total_numpages (thread_p, from_vol_id) * (INT64) IO_PAGESIZE;
}
/* print the number divided by volume pagesize */
npages = (int) CEIL_PTVDIV (session_p->dbfile.nbytes, IO_PAGESIZE);
backup_header_p = session_p->bkup.bkuphdr;
if (session_p->verbose_fp)
{
fprintf (session_p->verbose_fp, " %-28s | %10d | ", fileio_get_base_file_name (from_vol_label_p), npages);
}
/* set the number divied by backup pagesize */
if (last_page >= 0 && last_page < npages)
{
from_npages = CEIL_PTVDIV ((last_page + 1) * IO_PAGESIZE, backup_header_p->bkpagesize);
}
else
{
from_npages = (int) CEIL_PTVDIV (session_p->dbfile.nbytes, backup_header_p->bkpagesize);
}
/* Write a backup file header which identifies this volume/file on the backup. File headers do not use the extra
* pageid_copy field. */
session_p->dbfile.area->iopageid = FILEIO_BACKUP_FILE_START_PAGE_ID;
file_header_p = (FILEIO_BACKUP_FILE_HEADER *) (&session_p->dbfile.area->iopage);
file_header_p->volid = session_p->dbfile.volid;
file_header_p->nbytes = session_p->dbfile.nbytes;
strncpy (file_header_p->vlabel, session_p->dbfile.vlabel, PATH_MAX);
nread = FILEIO_BACKUP_FILE_HEADER_PAGE_SIZE;
if (fileio_write_backup (thread_p, session_p, nread) != NO_ERROR)
{
goto error;
}
#if defined(CUBRID_DEBUG)
/* How about adding a backup verbose option ... to print this sort of information as the backup is progressing? A DBA
* could later compare the information thus gathered with a restore -t option to verify the integrity of the archive. */
if (io_Bkuptrace_debug > 0)
{
fprintf (stdout, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_BKUP_FILE),
file_header_p->vlabel, file_header_p->volid, file_header_p->nbytes, CEIL_PTVDIV (file_header_p->nbytes,
IO_PAGESIZE));
fprintf (stdout, "\n");
}
#endif /* CUBRID_DEBUG */
/* Now start reading each page and writing each page to the backup. */
if (session_p->verbose_fp)
{
check_ratio = 1;
check_npages = (int) (((float) from_npages / 25.0) * check_ratio);
}
thread_info_p = &session_p->read_thread_info;
queue_p = &thread_info_p->io_queue;
/* set the number of activated read threads */
thread_info_p->act_r_threads = MAX (thread_info_p->num_threads - 1, 0);
thread_info_p->act_r_threads = MIN (thread_info_p->act_r_threads, from_npages);
if (thread_info_p->act_r_threads > 0)
{
#if defined(SERVER_MODE)
if (fileio_start_backup_thread (thread_p, session_p, thread_info_p, from_npages, is_only_updated_pages,
check_ratio, check_npages, queue_p) != NO_ERROR)
{
goto error;
}
if (session_p->verbose_fp)
{
check_ratio = thread_info_p->check_ratio;
}
#endif /* SERVER_MODE */
}
else
{
for (page_id = 0; page_id < from_npages; page_id++)
{
/* Have to allow other threads to run and check for interrupts from the user (i.e. Ctrl-C ). check for
* standalone-mode too. */
if ((page_id % FILEIO_CHECK_FOR_INTERRUPT_INTERVAL) == 0
&& pgbuf_is_log_check_for_interrupts (thread_p) == true)
{
goto error;
}
/* alloc queue node */
node_p = fileio_allocate_node (queue_p, backup_header_p);
if (node_p == NULL)
{
goto error;
}
/* read one page sequentially */
save_area_p = session_p->dbfile.area; /* save link */
session_p->dbfile.area = node_p->area;
node_p->pageid = page_id;
node_p->nread = fileio_read_backup (thread_p, session_p, node_p->pageid);
session_p->dbfile.area = save_area_p; /* restore link */
if (node_p->nread == -1)
{
goto error;
}
else if (node_p->nread == 0)
{
/* This could be an error since we estimated more pages. End of file/volume. */
(void) fileio_free_node (queue_p, node_p);
node_p = NULL;
break;
}
/*
* Do we need to backup this page ?
* In other words, has it been changed since either the previous backup
* of this level or a lower level.
*/
if (is_only_updated_pages == false || LSA_ISNULL (&session_p->dbfile.lsa)
|| LSA_LT (&session_p->dbfile.lsa, &node_p->area->iopage.prv.lsa))
{
/* Backup the content of this page along with its page identifier */
node_p->nread += FILEIO_BACKUP_PAGE_OVERHEAD;
FILEIO_SET_BACKUP_PAGE_ID_COPY (node_p->area, node_p->pageid, backup_header_p->bkpagesize);
#if defined(CUBRID_DEBUG)
fprintf (stdout, "fileio_backup_volume: %d\t%d,\t%d\n", ((FILEIO_BACKUP_PAGE *) (node_p->area))->iopageid,
*(PAGEID *) (((char *) (node_p->area)) + offsetof (FILEIO_BACKUP_PAGE, iopage) +
backup_header_p->bkpagesize), backup_header_p->bkpagesize);
#endif
if (backup_header_p->zip_method != FILEIO_ZIP_NONE_METHOD
&& fileio_compress_backup_node (node_p, backup_header_p) != NO_ERROR)
{
goto error;
}
save_area_p = session_p->dbfile.area; /* save link */
rv = fileio_write_backup_node (thread_p, session_p, node_p, backup_header_p);
session_p->dbfile.area = save_area_p; /* restore link */
if (rv != NO_ERROR)
{
goto error;
}
}
if (session_p->verbose_fp && from_npages >= 25 && page_id >= check_npages)
{
fprintf (session_p->verbose_fp, "#");
check_ratio++;
check_npages = (int) (((float) from_npages / 25.0) * check_ratio);
}
/* free node */
(void) fileio_free_node (queue_p, node_p);
node_p = NULL;
}
}
/* End of FILE */
/* alloc queue node */
node_p = fileio_allocate_node (queue_p, backup_header_p);
if (node_p == NULL)
{
goto error;
}
node_p->nread = FILEIO_DBVOLS_IO_PAGE_SIZE (backup_header_p);
memset (&node_p->area->iopage, '\0', backup_header_p->bkpagesize);
FILEIO_SET_BACKUP_PAGE_ID (node_p->area, FILEIO_BACKUP_FILE_END_PAGE_ID, backup_header_p->bkpagesize);
#if defined(CUBRID_DEBUG)
fprintf (stdout, "io_backup_volume: %d\t%d,\t%d\n", ((FILEIO_BACKUP_PAGE *) (node_p->area))->iopageid,
*(PAGEID *) (((char *) (node_p->area)) + offsetof (FILEIO_BACKUP_PAGE, iopage) +
backup_header_p->bkpagesize), backup_header_p->bkpagesize);
#endif
if (backup_header_p->zip_method != FILEIO_ZIP_NONE_METHOD
&& fileio_compress_backup_node (node_p, backup_header_p) != NO_ERROR)
{
goto error;
}
save_area_p = session_p->dbfile.area; /* save link */
rv = fileio_write_backup_node (thread_p, session_p, node_p, backup_header_p);
session_p->dbfile.area = save_area_p; /* restore link */
if (rv != NO_ERROR)
{
goto error;
}
/* free node */
(void) fileio_free_node (queue_p, node_p);
node_p = NULL;
#if defined(CUBRID_DEBUG)
fprintf (stdout, "volume EOF : bkpagesize = %d, voltotalio = %ld\n", backup_header_p->bkpagesize,
session_p->bkup.voltotalio);
#endif /* CUBRID_DEBUG */
/* Close the database volume/file */
if (is_need_vol_closed == true)
{
fileio_close (session_p->dbfile.vdes);
}
#if (defined(WINDOWS) || !defined(SERVER_MODE))
else
{
if (from_vol_id == LOG_DBLOG_ACTIVE_VOLID && session_p->dbfile.vdes != NULL_VOLDES && saved_act_log_fp >= 0)
{
/* restore file pointer */
lseek (session_p->dbfile.vdes, saved_act_log_fp, SEEK_SET);
}
}
#endif /* WINDOWS || !SERVER_MODE */
session_p->dbfile.vdes = NULL_VOLDES;
session_p->dbfile.volid = NULL_VOLID;
session_p->dbfile.nbytes = -1;
session_p->dbfile.vlabel = NULL;
if (session_p->verbose_fp)
{
if (from_npages < 25)
{
fprintf (session_p->verbose_fp, "######################### | done\n");
}
else
{
while (check_ratio <= 25)
{
fprintf (session_p->verbose_fp, "#");
check_ratio++;
}
fprintf (session_p->verbose_fp, " | done\n");
}
}
return NO_ERROR;
error:
if (is_need_vol_closed == true)
{
fileio_close (session_p->dbfile.vdes);
}
#if (defined(WINDOWS) || !defined(SERVER_MODE))
else
{
if (from_vol_id == LOG_DBLOG_ACTIVE_VOLID && session_p->dbfile.vdes != NULL_VOLDES && saved_act_log_fp >= 0)
{
/* restore file pointer */
lseek (session_p->dbfile.vdes, saved_act_log_fp, SEEK_SET);
}
}
#endif /* WINDOWS || !SERVER_MODE */
if (node_p != NULL)
{
(void) fileio_free_node (queue_p, node_p);
}
session_p->dbfile.vdes = NULL_VOLDES;
session_p->dbfile.volid = NULL_VOLID;
session_p->dbfile.nbytes = -1;
session_p->dbfile.vlabel = NULL;
return ER_FAILED;
}
#endif /* !CS_MODE */
/*
* fileio_flush_backup () - Flush any buffered data
* return:
* session(in/out): The session array
*
* Note: When the output fills up, we prompt for another volume or more space.
* Incomplete blocks are repeated at the start of the following archive,
* in order to insure that we do not try to read from incomplete tape
* blocks.
*/
static int
fileio_flush_backup (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session_p)
{
char *buffer_p;
ssize_t nbytes;
INT64 count;
bool is_interactive_need_new = false;
bool is_force_new_bkvol = false;
if (prm_get_bigint_value (PRM_ID_IO_BACKUP_MAX_VOLUME_SIZE) > 0
&& (UINT64) session_p->bkup.count > prm_get_bigint_value (PRM_ID_IO_BACKUP_MAX_VOLUME_SIZE))
{
er_log_debug (ARG_FILE_LINE, "Backup_flush: Backup aborted because count %d larger than max volume size %ld\n",
session_p->bkup.count, prm_get_bigint_value (PRM_ID_IO_BACKUP_MAX_VOLUME_SIZE));
return ER_FAILED;
}
#if defined(CUBRID_DEBUG)
fprintf (stdout, "io_backup_flush: bkup.count = %ld, voltotalio = %ld\n", session_p->bkup.count,
session_p->bkup.voltotalio);
#endif /* CUBRID_DEBUG */
/*
* Flush any buffered bytes.
* NOTE that we do not call fileio_write since it will try to do lseek and some
* backup devices do not seek.
*/
if (session_p->bkup.count > 0)
{
restart_newvol:
/*
* Determine number of bytes we can safely write to this volume
* being mindful of the max specified by the user.
*/
is_interactive_need_new = false;
is_force_new_bkvol = false;
count = session_p->bkup.count;
if (prm_get_bigint_value (PRM_ID_IO_BACKUP_MAX_VOLUME_SIZE) > 0)
{
count =
(int) MIN (count, prm_get_bigint_value (PRM_ID_IO_BACKUP_MAX_VOLUME_SIZE) - session_p->bkup.voltotalio);
}
buffer_p = session_p->bkup.buffer;
do
{
/* disk file size check */
if (session_p->bkup.voltotalio >= OFF_T_MAX && session_p->bkup.dtype == FILEIO_BACKUP_VOL_DIRECTORY)
{
/* New volume is needed */
is_force_new_bkvol = true;
}
else
{
/* Write the data */
nbytes = write (session_p->bkup.vdes, buffer_p, count);
if (nbytes <= 0)
{
if (nbytes == 0)
{
is_interactive_need_new = true; /* For raw partitions */
}
else
{
switch (errno)
{
/* equiv to try again */
case EINTR:
case EAGAIN:
continue;
/* New volume is needed and no user interaction needed */
#if !defined(WINDOWS)
case EDQUOT:
#endif /* !WINDOWS */
case EFBIG:
case EIO:
case EINVAL:
is_force_new_bkvol = true;
break;
/* New volume is needed and requires user interaction */
case ENXIO:
case ENOSPC:
case EPIPE:
is_interactive_need_new = true;
break;
/* equiv -- Failure */
default:
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_WRITE, 2,
CEIL_PTVDIV (session_p->bkup.voltotalio, IO_PAGESIZE),
session_p->bkup.vlabel);
return ER_FAILED;
}
}
}
else
{
session_p->bkup.voltotalio += nbytes;
count -= (int) nbytes;
buffer_p += nbytes;
}
}
if (is_interactive_need_new || is_force_new_bkvol
|| (prm_get_bigint_value (PRM_ID_IO_BACKUP_MAX_VOLUME_SIZE) > 0
&& ((UINT64) session_p->bkup.voltotalio >= prm_get_bigint_value (PRM_ID_IO_BACKUP_MAX_VOLUME_SIZE))))
{
#if defined(CUBRID_DEBUG)
fprintf (stdout, "open a new backup volume\n");
#endif /* CUBRID_DEBUG */
/* Finish this volume, fixup session and open a new volume */
if ((fileio_get_next_backup_volume (thread_p, session_p, is_interactive_need_new) != NO_ERROR)
|| (fileio_write_backup_header (session_p) != NO_ERROR))
{
return ER_FAILED;
}
else
{
/* Because the buffer may have been incompletely written on a raw device (i.e. tape), we start a new
* volume by repeating the current buffer block in its entirety immediately after the header. Upon
* restore, the incomplete part will be ignored. */
session_p->bkup.ptr = session_p->bkup.buffer;
goto restart_newvol;
}
}
}
while (count > 0);
/* Update session to reflect that flush completed. */
session_p->bkup.count = 0; /* can only update after flush completes */
session_p->bkup.ptr = session_p->bkup.buffer;
}
return NO_ERROR;
}
/*
* fileio_read_backup () - Read a database page from the current database
* volume/file that is backed up
* return:
* session(in/out): The session array
* pageid(in): The page from which we are reading
*
* Note: If we run into an end of file, we filled the page with nulls. This is
* needed since we write full pages to back up destination. Without this,
* we will not be able to know how much to read since not necessarily
* the whole volume/file is backed up.
*/
static ssize_t
fileio_read_backup (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session_p, int page_id)
{
int io_page_size = session_p->bkup.bkuphdr->bkpagesize;
#if defined(WINDOWS)
int nread, nbytes;
#else
ssize_t nread, nbytes;
#endif
char *buffer_p;
/* Read until you acumulate io_pagesize or the EOF mark is reached. */
nread = 0;
FILEIO_SET_BACKUP_PAGE_ID (session_p->dbfile.area, page_id, io_page_size);
#if defined(CUBRID_DEBUG)
fprintf (stdout, "fileio_read_backup: %d\t%d,\t%d\n", ((FILEIO_BACKUP_PAGE *) (session_p->dbfile.area))->iopageid,
*(PAGEID *) (((char *) (session_p->dbfile.area)) + offsetof (FILEIO_BACKUP_PAGE, iopage) + io_page_size),
io_page_size);
#endif
buffer_p = (char *) &session_p->dbfile.area->iopage;
while (nread < io_page_size)
{
/* Read the desired amount of bytes */
#if !defined(SERVER_MODE)
nbytes = read (session_p->dbfile.vdes, buffer_p, io_page_size - nread);
#elif defined(WINDOWS)
nbytes = read (session_p->dbfile.vdes, buffer_p, io_page_size - nread);
#else
nbytes =
pread (session_p->dbfile.vdes, buffer_p, io_page_size - nread,
FILEIO_GET_FILE_SIZE (io_page_size, page_id) + nread);
#endif
if (nbytes == -1)
{
if (errno != EINTR)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_READ, 2,
FILEIO_GET_BACKUP_PAGE_ID (session_p->dbfile.area), session_p->dbfile.vlabel);
return -1;
}
}
else if (nbytes == 0)
{
if (nread > 0)
{
#if defined(CUBRID_DEBUG)
fprintf (stdout, "io_backup_read: io_pagesize = %d, nread = %d, voltotalio = %d : ADD FILLER\n",
io_page_size, nread, session_p->bkup.voltotalio);
#endif /* CUBRID_DEBUG */
/*
* We have a file that it is not multiples of io_pagesize.
* We need to add a filler. otherwise, we will not be able to
* find other files.
*/
memset (buffer_p, '\0', io_page_size - nread);
nread = io_page_size;
}
break;
}
nread += nbytes;
buffer_p += nbytes;
}
#if defined(SERVER_MODE)
/* Backup Thread is reading data/log pages slowly to avoid IO burst */
if (session_p->dbfile.volid == LOG_DBLOG_ACTIVE_VOLID
|| (session_p->dbfile.volid == LOG_DBLOG_ARCHIVE_VOLID && LOG_CS_OWN_WRITE_MODE (thread_p)))
{
; /* go ahead */
}
else
{
int sleep_msecs;
if (session_p->sleep_msecs > 0) /* priority 1 */
{
sleep_msecs = session_p->sleep_msecs;
}
else if (prm_get_integer_value (PRM_ID_IO_BACKUP_SLEEP_MSECS) > 0) /* priority 2 */
{
sleep_msecs = prm_get_integer_value (PRM_ID_IO_BACKUP_SLEEP_MSECS);
}
else
{
sleep_msecs = 0;
}
if (sleep_msecs > 0)
{
sleep_msecs = (int) (((double) sleep_msecs) / (ONE_M / io_page_size));
if (sleep_msecs > 0)
{
thread_sleep (sleep_msecs);
}
}
}
#endif
return nread;
}
/*
* fileio_write_backup () - Write the number of indicated bytes from the dbfile
* area to to the backup destination
* return:
* session(in/out): The session array
* towrite_nbytes(in): Number of bytes that must be written
*/
static int
fileio_write_backup (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session_p, ssize_t to_write_nbytes)
{
char *buffer_p;
ssize_t nbytes;
buffer_p = (char *) session_p->dbfile.area;
while (to_write_nbytes > 0)
{
/*
* Buffer as much as you can, so that the device I/O will go through
* without any problem. Remember some backup devices work only with
* a fixed I/O length. We cannot use io_backup_write because we may
* have been called recursively from there after the old volume filled.
*/
nbytes = CAST_BUFLEN (session_p->bkup.iosize - session_p->bkup.count);
if (nbytes > to_write_nbytes)
{
nbytes = to_write_nbytes;
}
memcpy (session_p->bkup.ptr, buffer_p, nbytes);
session_p->bkup.count += nbytes;
session_p->bkup.ptr += nbytes;
buffer_p += nbytes;
to_write_nbytes -= nbytes;
if (session_p->bkup.count >= session_p->bkup.iosize)
{
if (fileio_flush_backup (thread_p, session_p) != NO_ERROR)
{
return ER_FAILED;
}
}
}
return NO_ERROR;
}
/*
* fileio_write_backup_header () - Immediately write the backup header to the
* destination
* return:
* session(in/out): The session array
*
* Note: Note that unlike io_backup_write, we do not buffer, instead we
* write directly to the output destination the number of bytes
* in a bkuphdr. This insures that headers all have the same
* physical block size so we can read them properly. The main
* purpose of this routine is to write the headers in a tape
* friendly blocking factor such that we can be sure we can read
* them back in without knowing how the tape was written in the
* first place.
*/
static int
fileio_write_backup_header (FILEIO_BACKUP_SESSION * session_p)
{
char *buffer_p;
int count, nbytes;
/* Write immediately to the backup. We do not use fileio_write for the same reason io_backup_flush does not. */
count = FILEIO_BACKUP_HEADER_IO_SIZE;
buffer_p = (char *) session_p->bkup.bkuphdr;
do
{
nbytes = write (session_p->bkup.vdes, buffer_p, count);
if (nbytes == -1)
{
if (errno == EINTR || errno == EAGAIN)
{
continue;
}
if (errno == ENOSPC)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_WRITE_OUT_OF_SPACE, 2,
CEIL_PTVDIV (session_p->bkup.voltotalio, IO_PAGESIZE),
fileio_get_volume_label_by_fd (session_p->bkup.vdes, PEEK));
}
else
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_WRITE, 2,
CEIL_PTVDIV (session_p->bkup.voltotalio, IO_PAGESIZE), session_p->bkup.vlabel);
}
return ER_FAILED;
}
else
{
count -= nbytes;
buffer_p += nbytes;
}
}
while (count > 0);
session_p->bkup.voltotalio += FILEIO_BACKUP_HEADER_IO_SIZE;
return NO_ERROR;
}
/*
* fileio_initialize_restore () - Initialize the restore session structure with the given information
* return: session or NULL
* db_fullname(in): Name of the database to backup
* backup_src(in): Name of backup device (file or directory)
* session(in/out): The session array
* level(in): The presumed backup level
* restore_verbose_file_path(in):
* newvolpath(in): restore the database and log volumes to the path
* specified in the database-loc-file
*
* Note: Note that the user may choose a new location for the volume, so the
* contents of the backup source path may be set as a side effect.
*/
static FILEIO_BACKUP_SESSION *
fileio_initialize_restore (THREAD_ENTRY * thread_p, const char *db_full_name_p, char *backup_source_p,
FILEIO_BACKUP_SESSION * session_p, FILEIO_BACKUP_LEVEL level,
const char *restore_verbose_file_path, bool is_new_vol_path)
{
char orig_name[PATH_MAX];
strcpy (orig_name, backup_source_p);
/* First, make sure the volume given exists and we can access it. */
while (!fileio_is_volume_exist (backup_source_p))
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, backup_source_p);
fprintf (stdout, "%s\n", er_msg ());
/* Let user see original prompt name until good one is chosen */
strcpy (backup_source_p, orig_name);
if (fileio_find_restore_volume (thread_p, db_full_name_p, backup_source_p, FILEIO_INITIAL_BACKUP_UNITS, level,
MSGCAT_FILEIO_RESTORE_FIND_REASON) == FILEIO_RELOCATION_QUIT)
{
return NULL;
}
}
/* Backup session initialization is the same as restore for now, as long as the first backup volume already exists,
* which we just checked. */
session_p->type = FILEIO_BACKUP_READ; /* access backup device for read */
/* save database full-pathname specified in the database-loc-file */
strncpy_bufsize (session_p->bkup.loc_db_fullname, is_new_vol_path ? db_full_name_p : "");
return (fileio_initialize_backup (db_full_name_p, (const char *) backup_source_p, session_p, level,
restore_verbose_file_path, 0 /* no multi-thread */ , 0 /* no sleep */ ));
}
/*
* fileio_abort_restore () - The restore session is aborted
* return: void
* session(in/out): The session array
*/
void
fileio_abort_restore (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session_p)
{
fileio_abort_backup (thread_p, session_p, false);
}
/*
* fileio_read_restore () - The number of bytes to read from the backup destination
* return:
* session(in/out): The session array
* toread_nbytes(in): Number of bytes to read
*
* Note: Now handles reads which span volumes, as well as reading incomplete
* blocks at the end of one volume. See fileio_flush_backup for details
* about how the final block is repeated at the start of the new volumes.
*/
static int
fileio_read_restore (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session_p, int to_read_nbytes)
{
#if defined(WINDOWS)
int nbytes;
#else
ssize_t nbytes;
#endif
char *buffer_p;
const char *next_vol_p;
bool is_end_of_backup = false;
bool is_need_next_vol = false;
/* Read until you acumulate the desired number of bytes (a database page) or the EOF mark is reached. */
buffer_p = (char *) session_p->dbfile.area;
while (to_read_nbytes > 0 && is_end_of_backup == false)
{
if (session_p->bkup.count <= 0)
{
/*
* Read and buffer another backup page from the backup volume.
* Note that a backup page is not necessarily the same size as the
* database page.
*/
restart_newvol:
is_need_next_vol = false;
session_p->bkup.ptr = session_p->bkup.buffer;
session_p->bkup.count = session_p->bkup.iosize;
while (session_p->bkup.count > 0)
{
/* Read a backup I/O page. */
nbytes = read (session_p->bkup.vdes, session_p->bkup.ptr, (int) session_p->bkup.count);
if (nbytes <= 0)
{
/* An error or EOF was found */
if (nbytes == 0)
{
/* Perhaps the last page read in was the very last one */
if (FILEIO_GET_BACKUP_PAGE_ID (session_p->dbfile.area) == FILEIO_BACKUP_END_PAGE_ID)
{
is_end_of_backup = true;
break;
}
else
{
is_need_next_vol = true;
}
}
else
{
switch (errno)
{
case EINTR:
case EAGAIN:
continue;
case EINVAL:
case ENXIO:
#if !defined(WINDOWS)
case EOVERFLOW:
#endif /* !WINDOWS */
is_need_next_vol = true;
break;
default:
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_READ, 2,
CEIL_PTVDIV (session_p->bkup.voltotalio, IO_PAGESIZE),
session_p->bkup.vlabel);
return ER_FAILED;
}
}
}
if (is_need_next_vol)
{
next_vol_p =
fileio_get_backup_info_volume_name (session_p->bkup.bkuphdr->level,
session_p->bkup.bkuphdr->unit_num + 1,
FILEIO_FIRST_BACKUP_VOL_INFO);
if (next_vol_p == NULL)
{
if (session_p->bkup.dtype != FILEIO_BACKUP_VOL_DEVICE)
{
fileio_get_directory_path (session_p->bkup.current_path, session_p->bkup.vlabel);
next_vol_p = session_p->bkup.current_path;
}
}
else
{
if (!fileio_is_volume_exist (next_vol_p)
&& strncmp (next_vol_p, session_p->bkup.current_path,
strlen (session_p->bkup.current_path)) != 0)
{
/* assume the changed path, not bkvinf file */
next_vol_p = session_p->bkup.current_path;
}
}
/* Unmount current backup volume */
fileio_dismount_without_fsync (thread_p, session_p->bkup.vdes);
session_p->bkup.vdes = NULL_VOLDES;
/* Find and mount the next volume and continue. */
if (session_p->bkup.dtype == FILEIO_BACKUP_VOL_DEVICE || next_vol_p == NULL)
{
/* Probably a tape device, let user mount new one */
if (next_vol_p != NULL)
{
if (snprintf (session_p->bkup.name, PATH_MAX, "%s", next_vol_p) >= PATH_MAX)
{
return ER_FAILED;
}
}
if (fileio_find_restore_volume (thread_p, session_p->bkup.bkuphdr->db_fullname,
session_p->bkup.name, session_p->bkup.bkuphdr->unit_num + 1,
session_p->bkup.bkuphdr->level,
MSGCAT_FILEIO_RESTORE_FIND_REASON) == FILEIO_RELOCATION_QUIT)
{
return ER_FAILED;
}
}
else
{
if (snprintf (session_p->bkup.name, PATH_MAX, "%s", next_vol_p) >= PATH_MAX)
{
return ER_FAILED;
}
}
/* Reset session count, etc */
session_p->bkup.alltotalio += session_p->bkup.voltotalio;
session_p->bkup.voltotalio = 0;
session_p->bkup.count = 0;
/* Bump the unit number to find the next volume */
session_p->bkup.bkuphdr->unit_num++;
/* Open the next volume */
if (fileio_continue_restore (thread_p, session_p->bkup.bkuphdr->db_fullname,
session_p->bkup.bkuphdr->db_creation, session_p, false, true,
session_p->bkup.bkuphdr->start_time) == NULL)
{
return ER_FAILED;
}
/* reset ptr */
session_p->bkup.ptr = session_p->bkup.buffer;
/* add new backup volume info info new_bkvinf */
fileio_add_volume_to_backup_info (session_p->bkup.vlabel, session_p->bkup.bkuphdr->level,
session_p->bkup.bkuphdr->unit_num,
FILEIO_SECOND_BACKUP_VOL_INFO);
/* Retry the buffered read from the new volume */
goto restart_newvol;
}
}
else
{
/* Increase the amount of read bytes */
session_p->bkup.ptr += nbytes;
session_p->bkup.count -= nbytes;
session_p->bkup.voltotalio += nbytes;
}
}
/* Increase the buffered information */
session_p->bkup.ptr = session_p->bkup.buffer;
session_p->bkup.count = session_p->bkup.iosize - session_p->bkup.count;
}
/* Now copy the desired bytes */
nbytes = session_p->bkup.count;
if (nbytes > to_read_nbytes)
{
nbytes = to_read_nbytes;
}
memcpy (buffer_p, session_p->bkup.ptr, nbytes);
session_p->bkup.count -= nbytes;
to_read_nbytes -= nbytes;
session_p->bkup.ptr += nbytes;
buffer_p += nbytes;
}
if (to_read_nbytes > 0 && !is_end_of_backup)
{
return ER_FAILED;
}
else
{
return NO_ERROR;
}
}
/*
* fileio_read_restore_header () - READ A BACKUP VOLUME HEADER
* return:
* session(in/out): The session array
*
* Note: This routine should be the first read from a backup volume or device.
* It reads the backup volume header that was written with the
* fileio_write_backup_header routine. The header was written with a
* specific buffer block size to be more compatible with tape devices so
* we can read it in without knowing how the rest of the data was
* buffered. Note this also means that backup volume headers are not the
* same size as FILEIO_BACKUP_PAGE anymore.
*/
static int
fileio_read_restore_header (FILEIO_BACKUP_SESSION * session_p)
{
int to_read_nbytes;
int nbytes;
char *buffer_p;
FILEIO_BACKUP_HEADER *backup_header_p;
backup_header_p = session_p->bkup.bkuphdr;
to_read_nbytes = FILEIO_BACKUP_HEADER_IO_SIZE;
buffer_p = (char *) backup_header_p;
while (to_read_nbytes > 0)
{
nbytes = read (session_p->bkup.vdes, buffer_p, to_read_nbytes);
if (nbytes == -1)
{
if (errno == EINTR)
{
continue;
}
else
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_READ, 2,
CEIL_PTVDIV (session_p->bkup.voltotalio, IO_PAGESIZE), session_p->bkup.vlabel);
return ER_FAILED;
}
}
else if (nbytes == 0)
{
/* EOF should not happen when reading the header. */
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_READ, 2,
CEIL_PTVDIV (session_p->bkup.voltotalio, IO_PAGESIZE), session_p->bkup.vlabel);
return ER_FAILED;
}
to_read_nbytes -= nbytes;
session_p->bkup.voltotalio += nbytes;
buffer_p += nbytes;
}
/* TODO: check for OLD version: no compression */
if (backup_header_p->bk_hdr_version == FILEIO_BACKUP_NO_ZIP_HEADER_VERSION)
{
backup_header_p->bkpagesize = backup_header_p->db_iopagesize;
backup_header_p->zip_method = FILEIO_ZIP_NONE_METHOD;
backup_header_p->zip_level = FILEIO_ZIP_NONE_LEVEL;
}
if (to_read_nbytes > 0)
{
return ER_FAILED;
}
else
{
return NO_ERROR;
}
}
/*
* fileio_start_restore () - Start a restore session
* return: session or NULL
* db_fullname(in): Name of the database to backup
* backup_source(in): Name of backup destination device (file or directory)
* match_dbcreation(out): Creation of data base of backup
* db_iopagesize(out): Database size of database in backup
* db_compatibility(out): Disk compatibility of database in backup
* session(in/out): The session array
* level(in): The presumed backup level
* authenticate(in): true when validation of new bkup volume header needed
* match_bkupcreation(in): explicit timestamp to match in new backup volume
* restore_verbose_file_path(in):
* newvolpath(in): restore the database and log volumes to the path
* specified in the database-loc-file
*/
FILEIO_BACKUP_SESSION *
fileio_start_restore (THREAD_ENTRY * thread_p, const char *db_full_name_p, char *backup_source_p,
INT64 match_db_creation_time, PGLENGTH * db_io_page_size_p, float *db_compatibility_p,
FILEIO_BACKUP_SESSION * session_p, FILEIO_BACKUP_LEVEL level, bool is_authenticate,
INT64 match_backup_creation_time, const char *restore_verbose_file_path, bool is_new_vol_path)
{
FILEIO_BACKUP_SESSION *temp_session_p;
/* Initialize the session array and open the backup source device. */
if (fileio_initialize_restore (thread_p, db_full_name_p, backup_source_p, session_p, level,
restore_verbose_file_path, is_new_vol_path) == NULL)
{
return NULL;
}
temp_session_p =
fileio_continue_restore (thread_p, db_full_name_p, match_db_creation_time, session_p, true, is_authenticate,
match_backup_creation_time);
if (temp_session_p != NULL)
{
*db_io_page_size_p = session_p->bkup.bkuphdr->db_iopagesize;
*db_compatibility_p = session_p->bkup.bkuphdr->db_compatibility;
}
return (temp_session_p);
}
static int
fileio_make_error_message (THREAD_ENTRY * thread_p, char *error_message_p)
{
char *header_message_p = NULL;
char *remote_message_p = NULL;
if (asprintf (&header_message_p, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO,
MSGCAT_FILEIO_INCORRECT_BKVOLUME)) < 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return ER_FAILED;
}
if (asprintf (&remote_message_p, "%s%s", header_message_p, error_message_p) < 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
free (header_message_p);
return ER_FAILED;
}
(void) fileio_request_user_response (thread_p, FILEIO_PROMPT_DISPLAY_ONLY, remote_message_p, NULL, NULL, -1, -1, NULL,
-1);
free (header_message_p);
free (remote_message_p);
return NO_ERROR;
}
/*
* fileio_continue_restore () - CONTINUE A RESTORE SESSION
* return: session or NULL
* db_fullname(in): Name of the database to backup
* db_creation(in): Creation of time data base
* session(in/out): The session array
* first_time(in): true the first time called during restore
* authenticate(in): true when validation of new bkup volume header needed
* match_bkupcreation(in): Creation time of backup
*
* Note: Called when a new backup volume is needed, this routine locates and
* opens the desired backup volume for this database. Also authenticates
* that it has the right timestamp, level, unit_num etc.
* The match_dbcreation parameter specifies an explicit bkup timestamp
* that must be matched. This is useful when one level is restored and
* the next is required. A zero for this variable will ignore the test.
*/
static FILEIO_BACKUP_SESSION *
fileio_continue_restore (THREAD_ENTRY * thread_p, const char *db_full_name_p, INT64 db_creation_time,
FILEIO_BACKUP_SESSION * session_p, bool is_first_time, bool is_authenticate,
INT64 match_backup_creation_time)
{
FILEIO_BACKUP_HEADER *backup_header_p;
int unit_num = FILEIO_INITIAL_BACKUP_UNITS;
PAGEID expect_page_id;
bool is_need_retry;
bool is_original_header = true;
char *error_message_p = NULL;
struct stat stbuf;
const char *db_nopath_name_p;
char copy_name[PATH_MAX];
char orig_name[PATH_MAX];
FILEIO_BACKUP_LEVEL level;
int exists;
int search_loop_count = 0;
char io_timeval[CTIME_MAX];
memset (io_timeval, 0, sizeof (io_timeval));
/* Note that for the first volume to be restored, bkuphdr must have been initialized with sensible defaults for these
* variables. */
unit_num = session_p->bkup.bkuphdr->unit_num;
level = session_p->bkup.bkuphdr->level;
do
{
is_need_retry = false;
/* Have to locate and open the desired volume */
while (session_p->bkup.vdes == NULL_VOLDES)
{
/* If the name chosen is a actually a directory, then append correct backup volume name here. */
exists = stat (session_p->bkup.vlabel, &stbuf) != -1;
if (session_p->bkup.dtype != FILEIO_BACKUP_VOL_DEVICE && (exists && S_ISDIR (stbuf.st_mode)))
{
db_nopath_name_p = fileio_get_base_file_name (db_full_name_p);
strcpy (copy_name, session_p->bkup.vlabel);
fileio_make_backup_name (session_p->bkup.name, db_nopath_name_p, copy_name, level, unit_num);
session_p->bkup.vlabel = session_p->bkup.name;
}
if (search_loop_count == 0)
{
strcpy (orig_name, session_p->bkup.vlabel);
}
session_p->bkup.vdes = fileio_open (session_p->bkup.vlabel, O_RDONLY, 0);
if (session_p->bkup.vdes == NULL_VOLDES)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, session_p->bkup.vlabel);
fprintf (stdout, "%s\n", er_msg ());
/* Since we cannot find what they were just looking for, reset the name to what we started looking for in
* the first place. */
strcpy (session_p->bkup.name, orig_name);
/* Attempt to locate the desired volume */
if (fileio_find_restore_volume (thread_p, db_full_name_p, session_p->bkup.name, unit_num, level,
MSGCAT_FILEIO_RESTORE_FIND_REASON) == FILEIO_RELOCATION_QUIT)
{
/* Cannot access backup file. Restore from backup is cancelled. */
er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_LOG_CANNOT_ACCESS_BACKUP, 1,
session_p->bkup.vlabel);
return NULL;
}
}
search_loop_count++;
}
/* Read description of the backup file. */
if (fileio_read_restore_header (session_p) != NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_NOT_A_BACKUP, 1, session_p->bkup.vlabel);
is_need_retry = true;
goto retry_newvol;
}
backup_header_p = session_p->bkup.bkuphdr;
/* check for restoring the database and log volumes to the path specified in the database-loc-file */
if (session_p->bkup.loc_db_fullname[0] != '\0')
{
/* replace db_fullname with the databases.txt info */
strncpy (backup_header_p->db_fullname, session_p->bkup.loc_db_fullname, PATH_MAX);
}
/* Always check for a valid magic number, regardless of whether we need to check other authentications. */
if (strcmp (backup_header_p->magic, CUBRID_MAGIC_DATABASE_BACKUP) != 0)
{
if (strcmp (backup_header_p->magic, CUBRID_MAGIC_DATABASE_BACKUP_OLD) == 0)
{
er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_LOG_BKUP_INCOMPATIBLE, 2, rel_name (),
rel_release_string ());
return NULL;
}
if (is_first_time)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_NOT_A_BACKUP, 1, session_p->bkup.vlabel);
return NULL;
}
else
{
if (asprintf (&error_message_p,
msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_MAGIC_MISMATCH),
session_p->bkup.vlabel) < 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return NULL;
}
if (fileio_make_error_message (thread_p, error_message_p) != NO_ERROR)
{
free (error_message_p);
return NULL;
}
free (error_message_p);
is_need_retry = true;
goto retry_newvol;
}
}
/* Should check the release version before we do anything */
if (is_first_time && rel_is_log_compatible (backup_header_p->db_release, rel_release_string ()) != true)
{
/*
* First time this database is restarted using the current version of
* CUBRID. Recovery should be done using the old version of the
* system
*/
er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_LOG_RECOVER_ON_OLD_RELEASE, 4, rel_name (),
backup_header_p->db_release, rel_release_string (), rel_release_string ());
return NULL;
}
if (is_authenticate)
{
if (is_first_time)
{
LSA_COPY (&session_p->dbfile.lsa, &backup_header_p->start_lsa);
}
if (level != backup_header_p->level)
{
if (asprintf (&error_message_p,
msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_LEVEL_MISMATCH),
session_p->bkup.vlabel, backup_header_p->level, level) < 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return NULL;
}
if (fileio_make_error_message (thread_p, error_message_p) != NO_ERROR)
{
free (error_message_p);
return NULL;
}
free (error_message_p);
is_need_retry = true;
goto retry_newvol;
}
/* Test the timestamp of when the backup was taken. */
if (match_backup_creation_time != 0
&& difftime ((time_t) match_backup_creation_time, (time_t) backup_header_p->start_time))
{
char save_time1[64];
fileio_ctime (&backup_header_p->start_time, io_timeval);
strcpy (save_time1, io_timeval);
fileio_ctime (&match_backup_creation_time, io_timeval);
if (asprintf (&error_message_p,
msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_BACKUP_TIME_MISMATCH),
session_p->bkup.vlabel, save_time1, io_timeval) < 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return NULL;
}
if (fileio_make_error_message (thread_p, error_message_p) != NO_ERROR)
{
free (error_message_p);
return NULL;
}
free (error_message_p);
is_need_retry = true;
goto retry_newvol;
}
/* Need to match the expected unit_num */
if (unit_num != backup_header_p->unit_num)
{
if (asprintf (&error_message_p,
msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_UNIT_NUM_MISMATCH),
session_p->bkup.vlabel, backup_header_p->unit_num, unit_num) < 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return NULL;
}
if (fileio_make_error_message (thread_p, error_message_p) != NO_ERROR)
{
free (error_message_p);
return NULL;
}
free (error_message_p);
is_need_retry = true;
goto retry_newvol;
}
/* Should this one be treated as fatal? */
expect_page_id = (is_first_time) ? FILEIO_BACKUP_START_PAGE_ID : FILEIO_BACKUP_VOL_CONT_PAGE_ID;
if (backup_header_p->iopageid != expect_page_id)
{
if (asprintf (&error_message_p,
msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_MAGIC_MISMATCH),
session_p->bkup.vlabel) < 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return NULL;
}
if (fileio_make_error_message (thread_p, error_message_p) != NO_ERROR)
{
free (error_message_p);
return NULL;
}
free (error_message_p);
is_need_retry = true;
goto retry_newvol;
}
/* NOTE: This could mess with restoring to a new location */
if (strcmp (backup_header_p->db_fullname, db_full_name_p) != 0
|| (db_creation_time > 0 && difftime ((time_t) db_creation_time, (time_t) backup_header_p->db_creation)))
{
if (is_first_time)
{
char save_time1[64];
char save_time2[64];
fileio_ctime (&backup_header_p->db_creation, io_timeval);
strcpy (save_time1, io_timeval);
fileio_ctime (&db_creation_time, io_timeval);
strcpy (save_time2, io_timeval);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_NOT_A_BACKUP_OF_GIVEN_DATABASE, 5,
session_p->bkup.vlabel, backup_header_p->db_fullname, save_time1, db_full_name_p, save_time2);
return NULL;
}
else
{
fileio_ctime (&backup_header_p->db_creation, io_timeval);
if (asprintf (&error_message_p,
msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_DB_MISMATCH),
session_p->bkup.vlabel, backup_header_p->db_fullname, io_timeval) < 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
return NULL;
}
if (fileio_make_error_message (thread_p, error_message_p) != NO_ERROR)
{
free (error_message_p);
return NULL;
}
free (error_message_p);
is_need_retry = true;
goto retry_newvol;
}
}
}
/* Passed all tests above */
break;
retry_newvol:
is_original_header = false;
/* close it, in case it was opened previously */
if (session_p->bkup.vdes != NULL_VOLDES)
{
fileio_close (session_p->bkup.vdes);
session_p->bkup.vdes = NULL_VOLDES;
}
/* Since there was a problem, let the user try again */
if (fileio_find_restore_volume (thread_p, db_full_name_p, session_p->bkup.name, unit_num, level,
MSGCAT_FILEIO_RESTORE_FIND_REASON) == FILEIO_RELOCATION_QUIT)
{
er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_LOG_CANNOT_ACCESS_BACKUP, 1, session_p->bkup.vlabel);
return NULL;
}
}
while (is_need_retry);
backup_header_p = session_p->bkup.bkuphdr;
/*
* If we read an archive header and notice that the buffer size
* was different than our current bkup.iosize then we will have
* to REALLOC the io areas set up in _init. Same for the
* when the database IO pagesize changes.
*/
if (backup_header_p->bkup_iosize > session_p->bkup.iosize)
{
session_p->bkup.buffer = (char *) realloc (session_p->bkup.buffer, backup_header_p->bkup_iosize);
if (session_p->bkup.buffer == NULL)
{
return NULL;
}
session_p->bkup.ptr = session_p->bkup.buffer; /* reinit in case it moved */
}
/* Always use the saved size from the backup to restore with */
session_p->bkup.iosize = backup_header_p->bkup_iosize;
/* backuped page is bigger than the current DB pagesize. must resize read buffer */
if (is_first_time)
{
if (backup_header_p->db_iopagesize > IO_PAGESIZE)
{
int io_pagesize, size;
io_pagesize = backup_header_p->db_iopagesize;
if (session_p->dbfile.level == FILEIO_BACKUP_FULL_LEVEL)
{
io_pagesize *= FILEIO_FULL_LEVEL_EXP;
}
size = MAX (io_pagesize + FILEIO_BACKUP_PAGE_OVERHEAD, FILEIO_BACKUP_FILE_HEADER_PAGE_SIZE);
free_and_init (session_p->dbfile.area);
session_p->dbfile.area = (FILEIO_BACKUP_PAGE *) malloc (size);
if (session_p->dbfile.area == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) size);
return NULL;
}
}
}
return session_p;
}
/*
* fileio_finish_restore () - Finish the restore session
* return:
* session(in/out): The session array
*/
int
fileio_finish_restore (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session_p)
{
int success;
success = fileio_synchronize_all (thread_p);
fileio_abort_restore (thread_p, session_p);
return success;
}
/*
* fileio_list_restore () - List description of current backup source
* return: session or NULL
* db_fullname(in): Name of the database to backup
* backup_source(out): Name of backup source device (file or directory)
* level(in): The presumed backup level
* newvolpath(in): restore the database and log volumes to the path
* specified in the database-loc-file
*/
int
fileio_list_restore (THREAD_ENTRY * thread_p, const char *db_full_name_p, char *backup_source_p,
FILEIO_BACKUP_LEVEL level, bool is_new_vol_path)
{
FILEIO_BACKUP_SESSION backup_session;
FILEIO_BACKUP_SESSION *session_p = &backup_session;
FILEIO_BACKUP_HEADER *backup_header_p;
FILEIO_BACKUP_FILE_HEADER *file_header_p;
PGLENGTH db_iopagesize;
float db_compatibility;
int nbytes, i;
INT64 db_creation_time = 0;
char file_name[PATH_MAX];
time_t tmp_time;
char time_val[CTIME_MAX];
if (fileio_start_restore (thread_p, db_full_name_p, backup_source_p, db_creation_time, &db_iopagesize,
&db_compatibility, session_p, level, false, 0, NULL, is_new_vol_path) == NULL)
{
/* Cannot access backup file.. Restore from backup is cancelled */
if (er_errid () == ER_GENERIC_ERROR)
{
er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_LOG_CANNOT_ACCESS_BACKUP, 1, backup_source_p);
}
return ER_FAILED;
}
/* First backup header was just read */
backup_header_p = session_p->bkup.bkuphdr;
/* this check is probably redundant */
if (backup_header_p->iopageid != FILEIO_BACKUP_START_PAGE_ID
&& backup_header_p->iopageid != FILEIO_BACKUP_VOL_CONT_PAGE_ID)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_NOT_A_BACKUP, 1, session_p->bkup.vlabel);
goto error;
}
/* Show the backup volume header information. */
fprintf (stdout, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_BKUP_HDR));
tmp_time = (time_t) backup_header_p->db_creation;
(void) ctime_r (&tmp_time, time_val);
fprintf (stdout, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_BKUP_HDR_DBINFO),
backup_header_p->db_fullname, time_val, backup_header_p->db_iopagesize);
fprintf (stdout, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_BKUP_HDR_LEVEL),
backup_header_p->level, fileio_get_backup_level_string (backup_header_p->level),
backup_header_p->start_lsa.pageid, backup_header_p->start_lsa.offset, backup_header_p->chkpt_lsa.pageid,
backup_header_p->chkpt_lsa.offset);
tmp_time = (time_t) backup_header_p->start_time;
(void) ctime_r (&tmp_time, time_val);
fprintf (stdout, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_BKUP_HDR_TIME), time_val,
backup_header_p->unit_num);
fprintf (stdout, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_BKUP_HDR_RELEASES),
backup_header_p->db_release, backup_header_p->db_compatibility);
fprintf (stdout, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_BKUP_HDR_BKUP_PAGESIZE),
backup_header_p->bkpagesize);
fprintf (stdout, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_BKUP_HDR_ZIP_INFO),
backup_header_p->zip_method, fileio_get_zip_method_string (backup_header_p->zip_method),
backup_header_p->zip_level, fileio_get_zip_level_string (backup_header_p->zip_level));
fprintf (stdout, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_BKUP_HDR_INC_ACTIVELOG),
backup_header_p->skip_activelog ? "NO" : "YES");
for (i = FILEIO_BACKUP_FULL_LEVEL; i < FILEIO_BACKUP_UNDEFINED_LEVEL && backup_header_p->previnfo[i].at_time > 0; i++)
{
tmp_time = (time_t) backup_header_p->previnfo[i].at_time;
(void) ctime_r (&tmp_time, time_val);
fprintf (stdout, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_BKUP_HDR_LX_LSA), i,
time_val, backup_header_p->previnfo[i].lsa.pageid, backup_header_p->previnfo[i].lsa.offset);
}
if (strlen (backup_header_p->db_prec_bkvolname) > 0)
{
fprintf (stdout, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_BKUP_PREV_BKVOL),
backup_header_p->db_prec_bkvolname);
}
/* Reminder this is not implemented yet, so no need to show it */
if (strlen (backup_header_p->db_next_bkvolname) > 0)
{
fprintf (stdout, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_BKUP_NEXT_BKVOL),
backup_header_p->db_next_bkvolname);
}
fprintf (stdout, "\n");
/* If this is not the first tape, then the header information of the backup is all we show. */
if (backup_header_p->unit_num != FILEIO_INITIAL_BACKUP_UNITS)
{
return fileio_finish_restore (thread_p, session_p);
}
/* Start reading information of every database volumes/files of the database which is in backup. */
file_header_p = (FILEIO_BACKUP_FILE_HEADER *) (&session_p->dbfile.area->iopage);
while (true)
{
nbytes = FILEIO_BACKUP_FILE_HEADER_PAGE_SIZE;
if (fileio_read_restore (thread_p, session_p, nbytes) != NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_RESTORE_READ_ERROR, 1, session_p->bkup.bkuphdr->unit_num);
goto error;
}
if (FILEIO_GET_BACKUP_PAGE_ID (session_p->dbfile.area) == FILEIO_BACKUP_END_PAGE_ID)
{
break;
}
if (FILEIO_GET_BACKUP_PAGE_ID (session_p->dbfile.area) != FILEIO_BACKUP_FILE_START_PAGE_ID)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_BKUP_DATABASE_VOLUME_OR_FILE_EXPECTED, 0);
goto error;
}
fprintf (stdout, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_BKUP_FILE),
file_header_p->vlabel, file_header_p->volid, file_header_p->nbytes, CEIL_PTVDIV (file_header_p->nbytes,
IO_PAGESIZE));
session_p->dbfile.volid = file_header_p->volid;
session_p->dbfile.nbytes = file_header_p->nbytes;
strncpy (file_name, file_header_p->vlabel, PATH_MAX);
session_p->dbfile.vlabel = file_name;
/* Read all file pages until the end of the file */
if (fileio_skip_restore_volume (thread_p, session_p) != NO_ERROR)
{
goto error;
}
}
fprintf (stdout, "\n");
return fileio_finish_restore (thread_p, session_p);
error:
fileio_abort_restore (thread_p, session_p);
return ER_FAILED;
}
/*
* fileio_get_backup_volume () - Get backup volume
* return: session or NULL
* db_fullname(in): Name of the database to backup
* logpath(in): Directory where the log volumes reside
* user_backuppath(in): Backup path that user specified
* from_volbackup (out) : Name of the backup volume
*
*/
int
fileio_get_backup_volume (THREAD_ENTRY * thread_p, const char *db_fullname, const char *logpath,
const char *user_backuppath, int try_level, char *from_volbackup)
{
FILE *backup_volinfo_fp = NULL; /* Pointer to backup */
const char *nopath_name; /* Name without path */
const char *volnameptr;
int retry;
int error_code = NO_ERROR;
char format_string[64];
struct stat stbuf;
sprintf (format_string, "%%%ds", PATH_MAX - 1);
nopath_name = fileio_get_base_file_name (db_fullname);
fileio_make_backup_volume_info_name (from_volbackup, logpath, nopath_name);
while ((stat (from_volbackup, &stbuf) == -1) || (backup_volinfo_fp = fopen (from_volbackup, "r")) == NULL)
{
/*
* When user specifies an explicit location, the backup vinf
* file is optional.
*/
if (user_backuppath != NULL)
{
break;
}
/*
* Backup volume information is not online
*/
fprintf (stdout, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_LOG, MSGCAT_LOG_STARTS));
fprintf (stdout, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_LOG, MSGCAT_LOG_BACKUPINFO_NEEDED),
from_volbackup);
fprintf (stdout, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_LOG, MSGCAT_LOG_STARTS));
if (scanf ("%d", &retry) != 1)
{
retry = 0;
}
switch (retry)
{
case 0: /* quit */
/* Cannot access backup file.. Restore from backup is cancelled */
error_code = ER_LOG_CANNOT_ACCESS_BACKUP;
er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 1, from_volbackup);
return error_code;
case 2:
fprintf (stdout, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_LOG, MSGCAT_LOG_NEWLOCATION));
if (scanf (format_string, from_volbackup) != 1)
{
/* Cannot access backup file.. Restore from backup is cancelled */
error_code = ER_LOG_CANNOT_ACCESS_BACKUP;
er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 1, from_volbackup);
return error_code;
}
break;
case 1:
default:
break;
}
}
/*
* If we get to here, we can read the bkvinf file, OR one does not
* exist and it is not required.
*/
if (backup_volinfo_fp != NULL)
{
if (fileio_read_backup_info_entries (backup_volinfo_fp, FILEIO_FIRST_BACKUP_VOL_INFO) == NO_ERROR)
{
volnameptr =
fileio_get_backup_info_volume_name ((FILEIO_BACKUP_LEVEL) try_level, FILEIO_INITIAL_BACKUP_UNITS,
FILEIO_FIRST_BACKUP_VOL_INFO);
if (volnameptr != NULL)
{
strcpy (from_volbackup, volnameptr);
}
else
{
fileio_make_backup_name (from_volbackup, nopath_name, logpath, (FILEIO_BACKUP_LEVEL) try_level,
FILEIO_INITIAL_BACKUP_UNITS);
}
}
else
{
fclose (backup_volinfo_fp);
return ER_FAILED;
}
fclose (backup_volinfo_fp);
}
if (user_backuppath != NULL)
{
strncpy (from_volbackup, user_backuppath, PATH_MAX - 1);
}
return NO_ERROR;
}
/*
* fileio_get_next_restore_file () - Find information of next file to restore
* return: -1 A failure, 0 No more files to restore (End of BACKUP),
* 1 There is a file to restore
* session(in/out): The session array
* filename(out): the name of next file to restore
* volid(out): Identifier of the database volume/file to restore
* vol_nbytes(out): Nbytes of the database volume/file to restore
*/
int
fileio_get_next_restore_file (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session_p, char *file_name_p,
VOLID * vol_id_p)
{
FILEIO_BACKUP_FILE_HEADER *file_header_p;
int nbytes;
char file_path[PATH_MAX];
/* Read the next database volume and/or file to restore. */
file_header_p = (FILEIO_BACKUP_FILE_HEADER *) (&session_p->dbfile.area->iopage);
nbytes = FILEIO_BACKUP_FILE_HEADER_PAGE_SIZE;
if (fileio_read_restore (thread_p, session_p, nbytes) != NO_ERROR)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_RESTORE_READ_ERROR, 1, session_p->bkup.bkuphdr->unit_num);
return -1;
}
if (FILEIO_GET_BACKUP_PAGE_ID (session_p->dbfile.area) == FILEIO_BACKUP_END_PAGE_ID)
{
if (session_p->bkup.dtype == FILEIO_BACKUP_VOL_DEVICE)
{
fileio_read_backup_end_time_from_last_page (session_p);
}
return 0;
}
if (FILEIO_GET_BACKUP_PAGE_ID (session_p->dbfile.area) != FILEIO_BACKUP_FILE_START_PAGE_ID)
{
return -1;
}
session_p->dbfile.volid = file_header_p->volid;
session_p->dbfile.nbytes = file_header_p->nbytes;
session_p->dbfile.level = session_p->bkup.bkuphdr->level;
/* check for restoring the database and log volumes to the path specified in the database-loc-file */
if (session_p->bkup.loc_db_fullname[0] != '\0')
{
/* replace filename with the databases.txt info */
if ((session_p->dbfile.volid == LOG_DBLOG_BKUPINFO_VOLID) || (session_p->dbfile.volid == LOG_DBLOG_INFO_VOLID)
|| (session_p->dbfile.volid == LOG_DBLOG_ARCHIVE_VOLID)
|| (session_p->dbfile.volid == LOG_DBLOG_ACTIVE_VOLID))
{
sprintf (file_name_p, "%s%c%s", session_p->bkup.log_path, PATH_SEPARATOR,
fileio_get_base_file_name (file_header_p->vlabel));
}
else
{
fileio_get_directory_path (file_path, session_p->bkup.loc_db_fullname);
sprintf (file_name_p, "%s%c%s", file_path, PATH_SEPARATOR, fileio_get_base_file_name (file_header_p->vlabel));
}
}
else
{
strncpy (file_name_p, file_header_p->vlabel, PATH_MAX);
}
*vol_id_p = session_p->dbfile.volid;
return 1;
}
/*
* fileio_fill_hole_during_restore () - Fill in a hole found in the backup during
* a restore
* return:
* next_pageid(out):
* stop_pageid(in):
* session(in/out): The session array
* page_bitmap(in): Page bitmap to record which pages have already
* been restored
*
* Note: A hole is likely only for 2 reasons. After the system pages in
* permament temp volumes, or at the end of a volume if we stop backing
* up unallocated pages.
*/
static int
fileio_fill_hole_during_restore (THREAD_ENTRY * thread_p, int *next_page_id_p, int stop_page_id,
FILEIO_BACKUP_SESSION * session_p, FILEIO_RESTORE_PAGE_BITMAP * page_bitmap)
{
FILEIO_PAGE *malloc_io_pgptr = NULL;
if (malloc_io_pgptr == NULL)
{
malloc_io_pgptr = (FILEIO_PAGE *) malloc (IO_PAGESIZE);
if (malloc_io_pgptr == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) IO_PAGESIZE);
return ER_FAILED;
}
memset ((char *) malloc_io_pgptr, 0, IO_PAGESIZE);
(void) fileio_initialize_res (thread_p, malloc_io_pgptr, IO_PAGESIZE);
}
while (*next_page_id_p < stop_page_id)
{
/*
* We did not back up a page since it was deallocated, or there
* is a hole of some kind that must be filled in with correctly
* formatted pages.
*/
if (fileio_write_restore (thread_p, page_bitmap, session_p->dbfile.vdes, malloc_io_pgptr, session_p->dbfile.volid,
*next_page_id_p, session_p->dbfile.level) == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_RESTORE_READ_ERROR, 1, session_p->bkup.bkuphdr->unit_num);
return ER_FAILED;
}
*next_page_id_p += 1;
}
if (malloc_io_pgptr != NULL)
{
free_and_init (malloc_io_pgptr);
}
return NO_ERROR;
}
/*
* fileio_decompress_restore_volume () - The number of bytes to decompress/read
* from the backup destination
* return:
* session(in/out): The session array
* nbytes(in): Number of bytes to read
*/
static int
fileio_decompress_restore_volume (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session_p, int nbytes)
{
int error = NO_ERROR;
FILEIO_THREAD_INFO *thread_info_p;
FILEIO_QUEUE *queue_p;
FILEIO_BACKUP_HEADER *backup_header_p;
FILEIO_BACKUP_PAGE *save_area_p;
FILEIO_NODE *node;
assert (nbytes >= 0);
thread_info_p = &session_p->read_thread_info;
queue_p = &thread_info_p->io_queue;
backup_header_p = session_p->bkup.bkuphdr;
node = NULL;
switch (backup_header_p->zip_method)
{
case FILEIO_ZIP_NONE_METHOD:
if (fileio_read_restore (thread_p, session_p, nbytes) != NO_ERROR)
{
error = ER_IO_RESTORE_READ_ERROR;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, backup_header_p->unit_num);
goto exit_on_error;
}
break;
case FILEIO_ZIP_LZ4_METHOD:
{
int rv;
FILEIO_ZIP_PAGE *zip_page;
/* alloc queue node */
node = fileio_allocate_node (queue_p, backup_header_p);
if (node == NULL)
{
goto exit_on_error;
}
assert (node->zip_info != NULL);
zip_page = &node->zip_info->zip_page;
save_area_p = session_p->dbfile.area; /* save link */
session_p->dbfile.area = (FILEIO_BACKUP_PAGE *) zip_page;
rv = fileio_read_restore (thread_p, session_p, sizeof (int));
session_p->dbfile.area = save_area_p; /* restore link */
if (rv != NO_ERROR)
{
error = ER_IO_RESTORE_READ_ERROR;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, backup_header_p->unit_num);
goto exit_on_error;
}
/* sanity check of the size values */
if (zip_page->buf_len > nbytes || zip_page->buf_len == 0)
{
error = ER_IO_LZ4_COMPRESS_FAIL; /* may be compress fail */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 4, backup_header_p->zip_method,
fileio_get_zip_method_string (backup_header_p->zip_method), backup_header_p->zip_level,
fileio_get_zip_level_string (backup_header_p->zip_level));
#if defined(CUBRID_DEBUG)
fprintf (stdout, "io_restore_volume_decompress_read: block size error - data corrupted\n");
#endif /* CUBRID_DEBUG */
goto exit_on_error;
}
else if (zip_page->buf_len < nbytes)
{
/* read compressed block data */
int unzip_len;
save_area_p = session_p->dbfile.area; /* save link */
session_p->dbfile.area = (FILEIO_BACKUP_PAGE *) zip_page->buf;
rv = fileio_read_restore (thread_p, session_p, zip_page->buf_len);
session_p->dbfile.area = save_area_p; /* restore link */
if (rv != NO_ERROR)
{
error = ER_IO_RESTORE_READ_ERROR;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, backup_header_p->unit_num);
goto exit_on_error;
}
/* decompress - use safe decompressor as data might be corrupted during a file transfer */
// *INDENT-OFF*
unzip_len =
cubcompress::decompress<cubcompress::LZ4> ((const char *) zip_page->buf, zip_page->buf_len,
(char *) session_p->dbfile.area, nbytes);
// *INDENT-ON*
if (unzip_len < 0 || unzip_len != nbytes)
{
error = ER_IO_LZ4_DECOMPRESS_FAIL;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
#if defined(CUBRID_DEBUG)
fprintf (stdout, "io_restore_volume_decompress_read: compressed data violation\n");
#endif /* CUBRID_DEBUG */
goto exit_on_error;
}
}
else
{
/* no compressed block */
rv = fileio_read_restore (thread_p, session_p, zip_page->buf_len);
if (rv != NO_ERROR)
{
error = ER_IO_RESTORE_READ_ERROR;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, backup_header_p->unit_num);
goto exit_on_error;
}
}
}
break;
case FILEIO_ZIP_LZO1X_METHOD:
case FILEIO_ZIP_ZLIB_METHOD:
default:
error = ER_IO_RESTORE_READ_ERROR;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, backup_header_p->unit_num);
goto exit_on_error;
}
exit_on_end:
/* free node */
if (node)
{
(void) fileio_free_node (queue_p, node);
}
return error;
exit_on_error:
if (error == NO_ERROR)
{
error = ER_FAILED;
}
goto exit_on_end;
}
#if !defined(CS_MODE)
/*
* fileio_restore_volume () - Restore a volume/file of given database
* return:
* session_p(in/out): The session array
* to_vlabel_p(in): Restore the next file using this name
* verbose_to_vlabel_p(in): Printable volume name
* prev_vlabel_p(out): Previous restored file name
* page_bitmap(in): Page bitmap to record which pages have already
* been restored
* is_remember_pages(in): true if we need to track which pages are restored
*/
int
fileio_restore_volume (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session_p, char *to_vol_label_p,
char *verbose_to_vol_label_p, char *prev_vol_label_p, FILEIO_RESTORE_PAGE_BITMAP * page_bitmap,
bool is_remember_pages, bool & is_prev_vol_header_restored,
FILEIO_UNLINKED_VOLINFO_MAP & unlinked_volinfo)
{
int next_page_id = 0;
INT64 total_nbytes = 0;
int nbytes;
int from_npages, npages;
FILEIO_RESTORE_PAGE_BITMAP *bitmap;
int check_ratio = 0, check_npages = 0;
FILEIO_BACKUP_HEADER *backup_header_p = session_p->bkup.bkuphdr;
int unit;
int i;
char *buffer_p;
bool incremental_includes_volume_header = false;
npages = (int) CEIL_PTVDIV (session_p->dbfile.nbytes, IO_PAGESIZE);
session_p->dbfile.vlabel = to_vol_label_p;
nbytes = (int) MIN (backup_header_p->bkpagesize, session_p->dbfile.nbytes);
unit = nbytes / IO_PAGESIZE;
if (nbytes % IO_PAGESIZE)
{
unit++;
}
#if defined(CUBRID_DEBUG)
if (io_Bkuptrace_debug > 0)
{
fprintf (stdout, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_BKUP_FILE),
session_p->dbfile.vlabel, session_p->dbfile.volid, session_p->dbfile.nbytes,
CEIL_PTVDIV (session_p->dbfile.nbytes, IO_PAGESIZE));
fprintf (stdout, "\n");
}
#endif /* CUBRID_DEBUG */
if (session_p->verbose_fp)
{
fprintf (session_p->verbose_fp, " %-28s | %10d | ", fileio_get_base_file_name (verbose_to_vol_label_p), npages);
check_ratio = 1;
check_npages = (int) (((float) npages / 25.0) * check_ratio);
}
/*
* Reformatting the volume guarantees no pollution from old contents.
* Note that for incremental restores, one can only reformat the volume
* once ... the first time that volume is replaced. This is needed
* because we are applying the restoration in reverse time order.
*/
if (!fileio_is_volume_exist (session_p->dbfile.vlabel))
{
session_p->dbfile.vdes =
fileio_format (thread_p, NULL, session_p->dbfile.vlabel, session_p->dbfile.volid, npages, false, false, false,
IO_PAGESIZE, 0, false);
}
else
{
session_p->dbfile.vdes =
fileio_mount (thread_p, NULL, session_p->dbfile.vlabel, session_p->dbfile.volid, false, false);
}
if (session_p->dbfile.vdes == NULL_VOLDES)
{
goto error;
}
/* For some volumes we do not keep track of the individual pages restored. */
bitmap = (is_remember_pages) ? page_bitmap : NULL;
/* Read all file pages until the end of the volume/file. */
from_npages = (int) CEIL_PTVDIV (session_p->dbfile.nbytes, backup_header_p->bkpagesize);
nbytes = FILEIO_RESTORE_DBVOLS_IO_PAGE_SIZE (session_p);
while (true)
{
if (fileio_decompress_restore_volume (thread_p, session_p, nbytes) != NO_ERROR)
{
goto error;
}
if (FILEIO_GET_BACKUP_PAGE_ID (session_p->dbfile.area) == FILEIO_BACKUP_FILE_END_PAGE_ID)
{
/*
* End of File marker in backup, but may not be true end of file being
* restored so we have to continue filling in pages until the
* restored volume is finished.
*/
if (session_p->dbfile.level == FILEIO_BACKUP_FULL_LEVEL && next_page_id < npages)
{
if (fileio_fill_hole_during_restore (thread_p, &next_page_id, npages, session_p, bitmap) != NO_ERROR)
{
goto error;
}
}
break;
}
if (FILEIO_GET_BACKUP_PAGE_ID (session_p->dbfile.area) > from_npages)
{
/* Too many pages for this volume according to the file header */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_RESTORE_PAGEID_OUTOF_BOUNDS, 4, backup_header_p->unit_num,
FILEIO_GET_BACKUP_PAGE_ID (session_p->dbfile.area), from_npages, session_p->dbfile.volid);
goto error;
}
#if defined(CUBRID_DEBUG)
fprintf (stdout, "fileio_restore_volume: %d\t%d,\t%d\n",
((FILEIO_BACKUP_PAGE *) (session_p->dbfile.area))->iopageid,
*(PAGEID *) (((char *) (session_p->dbfile.area)) + offsetof (FILEIO_BACKUP_PAGE, iopage) +
backup_header_p->bkpagesize), backup_header_p->bkpagesize);
#endif
if (!FILEIO_CHECK_RESTORE_PAGE_ID (session_p->dbfile.area, backup_header_p->bkpagesize))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_RESTORE_READ_ERROR, 1, backup_header_p->unit_num);
goto error;
}
/* Check for holes and fill them (only for full backup level) */
if (session_p->dbfile.level == FILEIO_BACKUP_FULL_LEVEL
&& (next_page_id < FILEIO_GET_BACKUP_PAGE_ID (session_p->dbfile.area)))
{
if (fileio_fill_hole_during_restore (thread_p, &next_page_id, session_p->dbfile.area->iopageid, session_p,
bitmap) != NO_ERROR)
{
goto error;
}
}
/* Restore the page we just read in */
if (session_p->dbfile.level != FILEIO_BACKUP_FULL_LEVEL)
{
next_page_id = FILEIO_GET_BACKUP_PAGE_ID (session_p->dbfile.area);
if (next_page_id == DISK_VOLHEADER_PAGE)
{
incremental_includes_volume_header = true;
}
}
buffer_p = (char *) &session_p->dbfile.area->iopage;
for (i = 0; i < unit && next_page_id < npages; i++)
{
if (fileio_write_restore (thread_p, bitmap, session_p->dbfile.vdes, buffer_p + i * IO_PAGESIZE,
session_p->dbfile.volid, next_page_id, session_p->dbfile.level) == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_RESTORE_READ_ERROR, 1, backup_header_p->unit_num);
goto error;
}
next_page_id += 1;
total_nbytes += IO_PAGESIZE;
if (session_p->verbose_fp && npages >= 25 && next_page_id >= check_npages)
{
fprintf (session_p->verbose_fp, "#");
check_ratio++;
check_npages = (int) (((float) npages / 25.0) * check_ratio);
}
}
}
if (total_nbytes > session_p->dbfile.nbytes && session_p->dbfile.volid < LOG_DBFIRST_VOLID)
{
(void) ftruncate (session_p->dbfile.vdes, session_p->dbfile.nbytes);
}
#if defined(CUBRID_DEBUG)
if (io_Bkuptrace_debug >= 2 && bitmap)
{
fileio_page_bitmap_dump (stdout, bitmap);
(void) fprintf (stdout, "\n\n");
}
#endif /* CUBRID_DEBUG */
/* check for restoring the database and log volumes to the path specified in the database-loc-file */
if (session_p->bkup.loc_db_fullname[0] != '\0' && session_p->dbfile.volid >= LOG_DBFIRST_VOLID)
{
/* Volume header page may not be included in incremental backup volumes.
* This means that volume header of a partially restoredb volume may not exist.
*/
if (session_p->dbfile.level == FILEIO_BACKUP_FULL_LEVEL || incremental_includes_volume_header == true)
{
VOLID volid;
volid = session_p->dbfile.volid;
if (disk_set_creation (thread_p, volid, to_vol_label_p, &backup_header_p->db_creation,
&session_p->bkup.last_chkpt_lsa, false, DISK_FLUSH_AND_INVALIDATE) != NO_ERROR)
{
goto error;
}
if (volid != LOG_DBFIRST_VOLID && is_prev_vol_header_restored)
{
VOLID prev_volid;
int prev_vdes;
/* previous vol */
prev_volid = fileio_find_previous_perm_volume (thread_p, volid);
prev_vdes = fileio_mount (thread_p, NULL, prev_vol_label_p, prev_volid, false, false);
if (prev_vdes == NULL_VOLDES)
{
goto error;
}
if (disk_set_link (thread_p, prev_volid, volid, to_vol_label_p, false, DISK_FLUSH_AND_INVALIDATE) !=
NO_ERROR)
{
fileio_dismount (thread_p, prev_vdes);
goto error;
}
fileio_dismount (thread_p, prev_vdes);
if (unlinked_volinfo.count (volid))
{
// The volume headers of both previous and current volumes are in the full or big incremental backup volumes.
// Therefore, the link between the two volumes is naturally established during the restoration process.
// So, there is no need to explicitly set it.
unlinked_volinfo.erase (volid);
_er_log_debug (ARG_FILE_LINE, "RESTOREDB: [UNSAVE UNLINK] [lv%d] volid=%d, vol=%s, prev_vol=%s\n",
session_p->dbfile.level, volid, to_vol_label_p, prev_vol_label_p);
}
}
if (incremental_includes_volume_header == true)
{
if (volid != LOG_DBFIRST_VOLID && is_prev_vol_header_restored == false)
{
_er_log_debug (ARG_FILE_LINE, "RESTOREDB: [FOUND UNLINK] [lv%d] volid=%d, vol=%s, prev_vol=%s\n",
session_p->dbfile.level, volid, to_vol_label_p, prev_vol_label_p);
if (!unlinked_volinfo.count (volid))
{
unlinked_volinfo[volid] =
std::make_pair (std::string (to_vol_label_p), std::string (prev_vol_label_p));
_er_log_debug (ARG_FILE_LINE, "RESTOREDB: [SAVE UNLINK] [lv%d] volid=%d, vol=%s, prev_vol=%s\n",
session_p->dbfile.level, volid, to_vol_label_p, prev_vol_label_p);
}
}
}
is_prev_vol_header_restored = true;
}
else
{
is_prev_vol_header_restored = false;
}
/* save current volname */
strncpy (prev_vol_label_p, to_vol_label_p, PATH_MAX);
}
fileio_dismount (thread_p, session_p->dbfile.vdes);
session_p->dbfile.vdes = NULL_VOLDES;
session_p->dbfile.volid = NULL_VOLID;
session_p->dbfile.vlabel = NULL;
if (session_p->verbose_fp)
{
if (next_page_id < 25)
{
fprintf (session_p->verbose_fp, "######################### | done\n");
}
else
{
while (check_ratio <= 25)
{
fprintf (session_p->verbose_fp, "#");
check_ratio++;
}
fprintf (session_p->verbose_fp, " | done\n");
}
}
return NO_ERROR;
error:
if (session_p->dbfile.vdes != NULL_VOLDES)
{
fileio_dismount (thread_p, session_p->dbfile.vdes);
}
session_p->dbfile.vdes = NULL_VOLDES;
session_p->dbfile.volid = NULL_VOLID;
session_p->dbfile.vlabel = NULL;
return ER_FAILED;
}
#endif /* !CS_MODE */
/*
* fileio_write_restore () - Write the content of the page described by pageid
* to disk
* return: o_pgptr on success, NULL on failure
* page_bitmap(in): Page bitmap to record which pages have already
* been restored
* vdes(in): Volume descriptor
* io_pgptr(in): In-memory address where the current content of page resides
* vol_id(in): volume identifier
* page_id(in): Page identifier
* level(in): backup level page restored from
*
* Note: The contents of the page stored on io_pgptr buffer which is
* IO_PAGESIZE long are sent to disk using fileio_write. The restore pageid
* cache is updated.
*/
static void *
fileio_write_restore (THREAD_ENTRY * thread_p, FILEIO_RESTORE_PAGE_BITMAP * page_bitmap, int vol_fd, void *io_page_p,
VOLID vol_id, PAGEID page_id, FILEIO_BACKUP_LEVEL level)
{
bool is_set;
FILEIO_WRITE_MODE write_mode = FILEIO_WRITE_DEFAULT_WRITE;
#if !defined (CS_MODE)
write_mode = dwb_is_created () == true ? FILEIO_WRITE_NO_COMPENSATE_WRITE : FILEIO_WRITE_DEFAULT_WRITE;
#endif
if (page_bitmap == NULL)
{
/* don't care about ht for this volume */
if (fileio_write (thread_p, vol_fd, io_page_p, page_id, IO_PAGESIZE, write_mode) == NULL)
{
return NULL;
}
}
else
{
#if !defined(NDEBUG)
assert (page_bitmap->vol_id == vol_id);
#endif
is_set = fileio_page_bitmap_is_set (page_bitmap, page_id);
if (!is_set)
{
if (fileio_write (thread_p, vol_fd, io_page_p, page_id, IO_PAGESIZE, write_mode) == NULL)
{
return NULL;
}
if (level > FILEIO_BACKUP_FULL_LEVEL)
{
fileio_page_bitmap_set (page_bitmap, page_id);
}
}
}
return io_page_p;
}
/*
* fileio_skip_restore_volume () - Skip over the next db volume from the backup
* during a restore
* return:
* session(in/out): The session array
*
* Note: Basically have to read all of the pages until we get to the end of
* the current backup file. It is necessary to "fast forward" to the
* next backup meta-data.
*/
int
fileio_skip_restore_volume (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session_p)
{
int nbytes;
FILEIO_BACKUP_HEADER *backup_header_p = session_p->bkup.bkuphdr;
/* Read all file pages until the end of the volume/file. */
nbytes = FILEIO_RESTORE_DBVOLS_IO_PAGE_SIZE (session_p);
while (true)
{
if (fileio_decompress_restore_volume (thread_p, session_p, nbytes) != NO_ERROR)
{
goto error;
}
if (FILEIO_GET_BACKUP_PAGE_ID (session_p->dbfile.area) == FILEIO_BACKUP_FILE_END_PAGE_ID)
{
/* End of FILE */
break;
}
#if defined(CUBRID_DEBUG)
fprintf (stdout, "fileio_skip_restore_volume: %d\t%d,\t%d\n",
((FILEIO_BACKUP_PAGE *) (session_p->dbfile.area))->iopageid,
*(PAGEID *) (((char *) (session_p->dbfile.area)) + offsetof (FILEIO_BACKUP_PAGE, iopage) +
backup_header_p->bkpagesize), backup_header_p->bkpagesize);
#endif
if (!FILEIO_CHECK_RESTORE_PAGE_ID (session_p->dbfile.area, backup_header_p->bkpagesize))
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_RESTORE_READ_ERROR, 1, backup_header_p->unit_num);
goto error;
}
}
session_p->dbfile.vdes = NULL_VOLDES;
session_p->dbfile.volid = NULL_VOLID;
session_p->dbfile.vlabel = NULL;
return NO_ERROR;
error:
session_p->dbfile.vdes = NULL_VOLDES;
session_p->dbfile.volid = NULL_VOLID;
session_p->dbfile.vlabel = NULL;
return ER_FAILED;
}
/*
* fileio_find_restore_volume () - FIND NEW LOCATION OF A VOLUME TO RESTORE
* return:
* dbname(in): The name of the database to which the volume belongs
* to_volname(in): Name we think the volume is supposed to be
* unit_num(in):
* level(in): the backup level needed for restoration
* reason(in):
*
* Note: Prompt the user to tell us the path to backup volume we cannot seem
* to find. Note that validation is not done here it must be done by
* the caller.
*/
static FILEIO_RELOCATION_VOLUME
fileio_find_restore_volume (THREAD_ENTRY * thread_p, const char *db_name_p, char *to_vol_name_p, int unit_num,
FILEIO_BACKUP_LEVEL level, int reason)
{
char *ptr1 = NULL, *ptr2 = NULL, *ptr3 = NULL, *ptr4 = NULL;
char new_vol_name[FILEIO_MAX_USER_RESPONSE_SIZE];
char *fail_prompt_p = NULL;
char *reprompt_p = NULL;
char *full_message_p = NULL;
FILEIO_RELOCATION_VOLUME rval;
/* Try to build up the outgoing message */
if (asprintf (&ptr1, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_STARTS)) < 0
|| asprintf (&ptr2, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, reason)) < 0
|| asprintf (&ptr3, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_REST_RELO_NEEDED),
db_name_p, to_vol_name_p, unit_num, level, fileio_get_backup_level_string (level)) < 0
|| asprintf (&ptr4, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_REST_RELO_OPTIONS),
FILEIO_RELOCATION_QUIT, FILEIO_RELOCATION_RETRY, FILEIO_RELOCATION_ALTERNATE) < 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
rval = FILEIO_RELOCATION_QUIT;
goto end;
}
if (asprintf (&fail_prompt_p, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_INPUT_RANGE_ERROR),
(int) FILEIO_RELOCATION_FIRST, (int) FILEIO_RELOCATION_LAST) < 0
|| asprintf (&reprompt_p, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_NEWLOCATION)) < 0
|| asprintf (&full_message_p, "%s%s%s%s%s", ptr1, ptr2, ptr3, ptr4, ptr1) < 0)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
rval = FILEIO_RELOCATION_QUIT;
goto end;
}
if (fileio_request_user_response (thread_p, FILEIO_PROMPT_RANGE_WITH_SECONDARY_STRING_TYPE, full_message_p,
new_vol_name, fail_prompt_p, FILEIO_RELOCATION_QUIT, FILEIO_RELOCATION_ALTERNATE,
reprompt_p, FILEIO_RELOCATION_ALTERNATE) != NO_ERROR)
{
rval = FILEIO_RELOCATION_QUIT;
goto end;
}
/* interpret the user responses. */
if (new_vol_name[0] - '0' == FILEIO_RELOCATION_RETRY)
{
rval = FILEIO_RELOCATION_RETRY;
}
else if (new_vol_name[0] - '0' == FILEIO_RELOCATION_ALTERNATE)
{
rval = FILEIO_RELOCATION_ALTERNATE;
strcpy (to_vol_name_p, &new_vol_name[1]);
}
else
{
rval = FILEIO_RELOCATION_QUIT;
}
end:
if (ptr1)
{
free (ptr1);
}
if (ptr2)
{
free (ptr2);
}
if (ptr3)
{
free (ptr3);
}
if (ptr4)
{
free (ptr4);
}
if (fail_prompt_p)
{
free (fail_prompt_p);
}
if (reprompt_p)
{
free (reprompt_p);
}
if (full_message_p)
{
free (full_message_p);
}
return rval;
}
/*
* fileio_get_backup_level_string () - return the string name of the backup level
* return: pointer to string containing name of level
* level(in): the backup level to convert
*/
static const char *
fileio_get_backup_level_string (FILEIO_BACKUP_LEVEL level)
{
switch (level)
{
case FILEIO_BACKUP_FULL_LEVEL:
return ("FULL LEVEL");
case FILEIO_BACKUP_BIG_INCREMENT_LEVEL:
return ("INCREMENTAL LEVEL 1");
case FILEIO_BACKUP_SMALL_INCREMENT_LEVEL:
return ("INCREMENTAL LEVEL 2");
default:
return ("UNKNOWN");
}
}
/*
* fileio_get_zip_method_string () - return the string name of the compression method
* return: pointer to string containing name of zip_method
* zip_method(in): the compression method to convert
*/
const char *
fileio_get_zip_method_string (FILEIO_ZIP_METHOD zip_method)
{
switch (zip_method)
{
case FILEIO_ZIP_NONE_METHOD:
return ("NONE");
case FILEIO_ZIP_LZO1X_METHOD:
return ("LZO1X");
case FILEIO_ZIP_LZ4_METHOD:
return ("LZ4");
case FILEIO_ZIP_ZLIB_METHOD:
return ("ZLIB");
default:
return ("UNKNOWN");
}
}
/*
* fileio_get_zip_level_string () - return the string name of the compression level
* return: pointer to string containing name of zip_level
* zip_level(in): the compression level to convert
*/
const char *
fileio_get_zip_level_string (FILEIO_ZIP_LEVEL zip_level)
{
switch (zip_level)
{
case FILEIO_ZIP_NONE_LEVEL:
return ("NONE");
case FILEIO_ZIP_1_LEVEL:
return ("ZIP LEVEL 1");
default:
return ("UNKNOWN");
}
}
/*
* fileio_get_next_backup_volume () - FIND LOCATION OR NEW VOLUME NAME TO CONTINUE BACKUP
* return:
* session(in/out): The session array
* user_new(in): true if user must be involved in the switch
*
* Note: This routine halts output to the current backup volume and opens the
* next backup volume, creating it if it is a file. User interaction may
* be necessary in cases where the backup volume is really a device
* (i.e. because a tape must be mounted). User interaction may also be
* required if the disk space is full on the current disk. We must
* insure that new location chosen is large enough.
*/
static int
fileio_get_next_backup_volume (THREAD_ENTRY * thread_p, FILEIO_BACKUP_SESSION * session_p, bool is_new_user)
{
const char *db_nopath_name_p = NULL;
char copy_name[PATH_MAX];
char orig_name[PATH_MAX];
char *message_area_p = NULL;
char io_timeval[CTIME_MAX];
if (session_p->bkup.dtype == FILEIO_BACKUP_VOL_DIRECTORY)
{
fileio_dismount (thread_p, session_p->bkup.vdes);
}
else
{
fileio_dismount_without_fsync (thread_p, session_p->bkup.vdes);
}
session_p->bkup.vdes = NULL_VOLDES;
/* Always force a new one for devices */
if (session_p->bkup.dtype == FILEIO_BACKUP_VOL_DEVICE)
{
is_new_user = true;
}
/* Keep the backup info correct */
session_p->bkup.alltotalio += session_p->bkup.voltotalio;
session_p->bkup.voltotalio = 0;
/* Tell user that current backup volume just completed */
fileio_ctime (&session_p->bkup.bkuphdr->start_time, io_timeval);
if (asprintf (&message_area_p, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_BACKUP_LABEL_INFO),
session_p->bkup.bkuphdr->level, session_p->bkup.bkuphdr->unit_num,
fileio_get_base_file_name (session_p->bkup.bkuphdr->db_fullname), io_timeval) < 0)
{
/* Note: we do not know the exact malloc size that failed */
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) FILEIO_MAX_USER_RESPONSE_SIZE);
return ER_FAILED;
}
else
{
(void) fileio_request_user_response (thread_p, FILEIO_PROMPT_DISPLAY_ONLY, message_area_p, NULL, NULL, -1, -1,
NULL, -1);
/* Note: Not free_and_init */
free (message_area_p);
}
/* Some initializations for start of next backup volume. */
session_p->bkup.bkuphdr->iopageid = FILEIO_BACKUP_VOL_CONT_PAGE_ID;
session_p->bkup.bkuphdr->unit_num++;
memset (session_p->bkup.bkuphdr->db_prec_bkvolname, 0, sizeof (session_p->bkup.bkuphdr->db_prec_bkvolname));
strcpy (session_p->bkup.bkuphdr->db_prec_bkvolname, session_p->bkup.vlabel);
memset (session_p->bkup.bkuphdr->db_next_bkvolname, 0, sizeof (session_p->bkup.bkuphdr->db_next_bkvolname));
/* Guess new path name in same dir as current volume. For devices, just repeat the device name. */
if (session_p->bkup.dtype == FILEIO_BACKUP_VOL_DIRECTORY)
{
/* First, get the path of the just finished volume. */
fileio_get_directory_path (orig_name, session_p->bkup.name);
strcpy (copy_name, orig_name);
if (is_new_user)
{
/* Fill in the expected volume name to show the user */
db_nopath_name_p = fileio_get_base_file_name (session_p->bkup.bkuphdr->db_fullname);
fileio_make_backup_name (session_p->bkup.name, db_nopath_name_p, orig_name, session_p->bkup.bkuphdr->level,
session_p->bkup.bkuphdr->unit_num + 1);
strcpy (copy_name, session_p->bkup.name);
}
}
else
{
strcpy (copy_name, session_p->bkup.name);
strcpy (orig_name, session_p->bkup.name);
}
do
{
if (is_new_user)
{
if (fileio_find_restore_volume (thread_p, session_p->bkup.bkuphdr->db_fullname, copy_name,
session_p->bkup.bkuphdr->unit_num, session_p->dbfile.level,
MSGCAT_FILEIO_BKUP_FIND_REASON) == FILEIO_RELOCATION_QUIT)
{
er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_LOG_CANNOT_ACCESS_BACKUP, 1, session_p->bkup.vlabel);
return ER_FAILED;
}
}
/* Append backup volume name onto directory. */
if (session_p->bkup.dtype == FILEIO_BACKUP_VOL_DIRECTORY)
{
db_nopath_name_p = fileio_get_base_file_name (session_p->bkup.bkuphdr->db_fullname);
fileio_make_backup_name (session_p->bkup.name, db_nopath_name_p, copy_name, session_p->dbfile.level,
session_p->bkup.bkuphdr->unit_num);
}
else
{
strcpy (session_p->bkup.name, copy_name);
}
session_p->bkup.vlabel = session_p->bkup.name;
/* Create the new volume and go on with our lives */
session_p->bkup.vdes =
fileio_create_backup_volume (thread_p, session_p->bkup.bkuphdr->db_fullname, session_p->bkup.vlabel,
LOG_DBCOPY_VOLID, false, false,
((session_p->dbfile.level == FILEIO_BACKUP_FULL_LEVEL)
? FILEIO_BACKUP_MINIMUM_NUM_PAGES_FULL_LEVEL : FILEIO_BACKUP_MINIMUM_NUM_PAGES));
if (session_p->bkup.vdes < 0)
{
er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IO_MOUNT_FAIL, 1, session_p->bkup.vlabel);
if (asprintf (&message_area_p, "%s\n", er_msg ()) < 0)
{
fileio_request_user_response (thread_p, FILEIO_PROMPT_DISPLAY_ONLY, message_area_p, NULL, NULL, -1, -1,
NULL, -1);
free (message_area_p);
}
}
is_new_user = true;
/* reset to the original name until acceptable alternative is chosen */
strcpy (copy_name, orig_name);
}
while (session_p->bkup.vdes == NULL_VOLDES);
/* Remember name of new backup volume */
if (fileio_add_volume_to_backup_info (session_p->bkup.name, session_p->dbfile.level,
session_p->bkup.bkuphdr->unit_num, FILEIO_FIRST_BACKUP_VOL_INFO) != NO_ERROR)
{
return ER_FAILED;
}
return NO_ERROR;
}
/*
* BKVINF RELATED FUNCTIONS
*
* These functions collectively manage the bkvinf file. They are responsible
* for reading, writing, and maintaining the in memory cache of backup
* volume related information.
*/
/*
* fileio_add_volume_to_backup_info () - Add a backup volume name to the internal cache
* return:
* name(in): Filename of backup volume
* level(in): backup level for that volume
* unit_num(in): unit (or sequence) number of that volume
* which_bkvinf(in):
*/
int
fileio_add_volume_to_backup_info (const char *name_p, FILEIO_BACKUP_LEVEL level, int unit_num, int which_bkvinf)
{
FILEIO_BACKUP_INFO_ENTRY *node_p, *back_p;
struct stat stbuf;
char real_path_buf[PATH_MAX];
FILEIO_BACKUP_INFO_QUEUE *data_p;
data_p = &(fileio_Backup_vol_info_data[which_bkvinf]);
if ((!(*data_p).initialized) && fileio_initialize_backup_info (which_bkvinf) != NO_ERROR)
{
return ER_FAILED;
}
/* Get a node */
node_p = fileio_allocate_backup_info (which_bkvinf);
if (node_p == NULL)
{
return ER_FAILED;
}
if (stat (name_p, &stbuf) != -1)
{
if (S_ISREG (stbuf.st_mode))
{
/* for regular file, convert to the real-path */
if (realpath (name_p, real_path_buf) != NULL)
{
name_p = real_path_buf;
}
}
}
if (snprintf (node_p->bkvol_name, PATH_MAX, "%s", name_p) >= PATH_MAX)
{
return ER_FAILED;
}
node_p->unit_num = unit_num;
/* Put it on the queue for that level */
if ((*data_p).anchors[level] == NULL)
{
/* this is the first one */
(*data_p).anchors[level] = node_p;
}
else
{
/* Put it at the end of the chain */
back_p = (*data_p).anchors[level];
while (back_p->link != NULL)
{
if (back_p->unit_num == unit_num)
{
er_log_debug (ARG_FILE_LINE, "bkvinf inconsistency, duplicate unit num %d found for level %d\n",
unit_num, level);
}
back_p = back_p->link;
}
/* check the last entry */
if (back_p->unit_num == unit_num)
{
er_log_debug (ARG_FILE_LINE, "bkvinf inconsistency, duplicate unit num %d found for level %d\n", unit_num,
level);
}
back_p->link = node_p;
}
return NO_ERROR;
}
/*
* fileio_write_backup_info_entries () - Prints internal bkvinf table in a format
* suitable for the bkvinf file as well as human
* consumption
* return:
* fp(in): Open file handle or else NULL for stdout
* which_bkvinf(in):
*/
int
fileio_write_backup_info_entries (FILE * fp, int which_bkvinf)
{
FILEIO_BACKUP_INFO_ENTRY *node_p;
int level, n;
FILEIO_BACKUP_INFO_QUEUE *data_p;
data_p = &(fileio_Backup_vol_info_data[which_bkvinf]);
if (!fp)
{
fp = stdout;
}
for (level = FILEIO_BACKUP_FULL_LEVEL; level < FILEIO_BACKUP_UNDEFINED_LEVEL; level++)
{
for (node_p = (*data_p).anchors[level]; node_p != NULL; node_p = node_p->link)
{
n = fprintf (fp, "%3d %d %s\n", level, node_p->unit_num, node_p->bkvol_name);
if (n <= 0)
{
return ER_FAILED;
}
}
}
return NO_ERROR;
}
/*
* fileio_read_backup_info_entries () - Read and parse the entries in a bkvinf file and
* store them in the internal cache
* return:
* fp(in): Open file handle
* which_bkvinf(in):
*/
int
fileio_read_backup_info_entries (FILE * fp, int which_bkvinf)
{
FILEIO_BACKUP_LEVEL level;
int tmp, unit_num;
char vol_name[PATH_MAX];
int n, line = 0;
char format_string[32];
if (fp == NULL)
{
return ER_FAILED;
}
/* Always throw away old cache (if any) and then start fresh */
fileio_finalize_backup_info (which_bkvinf);
if (fileio_initialize_backup_info (which_bkvinf) != NO_ERROR)
{
return ER_FAILED;
}
sprintf (format_string, "%%d %%d %%%ds", PATH_MAX - 1);
while ((n = fscanf (fp, format_string, &tmp, &unit_num, vol_name)) > 0)
{
level = (FILEIO_BACKUP_LEVEL) tmp;
line++;
if ((n != 3) || (level >= FILEIO_BACKUP_UNDEFINED_LEVEL)
/* || (level < FILEIO_BACKUP_FULL_LEVEL) */
|| (unit_num < FILEIO_INITIAL_BACKUP_UNITS))
{
fprintf (stdout, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_IO, MSGCAT_FILEIO_BACKUP_VINF_ERROR),
line);
return ER_FAILED;
}
/* remember this backup volume */
if (fileio_add_volume_to_backup_info (vol_name, level, unit_num, which_bkvinf) != NO_ERROR)
{
return ER_FAILED;
}
}
return NO_ERROR;
}
/*
* fileio_get_backup_info_volume_name () - Return the string of volume name
* return:
* level(in):
* unit_num(in):
* which_bkvinf(in):
*/
const char *
fileio_get_backup_info_volume_name (FILEIO_BACKUP_LEVEL level, int unit_num, int which_bkvinf)
{
FILEIO_BACKUP_INFO_ENTRY *node_p;
FILEIO_BACKUP_INFO_QUEUE *data_p;
data_p = &(fileio_Backup_vol_info_data[which_bkvinf]);
for (node_p = (*data_p).anchors[level]; node_p != NULL; node_p = node_p->link)
{
if (unit_num == node_p->unit_num)
{
return (node_p->bkvol_name);
}
}
return NULL;
}
/*
* fileio_finalize_backup_info () - Clean up and free all resources related to the bkvinf
* cache
* return: void
* which_bkvinf(in):
*/
void
fileio_finalize_backup_info (int which_bkvinf)
{
FILEIO_BACKUP_INFO_QUEUE *data_p;
data_p = &(fileio_Backup_vol_info_data[which_bkvinf]);
if ((*data_p).initialized)
{
fileio_clear_backup_info_level (FILEIO_BACKUP_FULL_LEVEL, true, which_bkvinf);
(*data_p).initialized = false;
}
return;
}
/*
* fileio_initialize_backup_info () - Initialize the bkvinf cache and related structures
* return:
* which_bkvinf(in):
*
* Note: Be sure to call bkvinf_final when the backups are complete.
*/
static int
fileio_initialize_backup_info (int which_bkvinf)
{
FILEIO_BACKUP_INFO_QUEUE *data_p;
#if defined(CUBRID_DEBUG)
const char *env_value;
/* This checking is done here so it can be in one location and because this routine is likely to be called before
* backup/restore. */
if (io_Bkuptrace_debug < 0)
{
/* Find out if user wants debugging info during backup/restore. */
env_value = envvar_get ("IO_TRACE_BACKUP");
io_Bkuptrace_debug = (env_value == NULL ? 0 : atoi (env_value));
}
#endif /* CUBRID_DEBUG */
data_p = &(fileio_Backup_vol_info_data[which_bkvinf]);
if (!(*data_p).initialized)
{
(*data_p).free = NULL;
memset ((*data_p).anchors, 0, sizeof ((*data_p).anchors));
(*data_p).initialized = true;
}
return NO_ERROR;
}
/*
* fileio_allocate_backup_info () - Allocate a bkvinf_entry
* return: a pointer to the node allocated, or NULL if failure
* which_bkvinf(in):
*/
static FILEIO_BACKUP_INFO_ENTRY *
fileio_allocate_backup_info (int which_bkvinf)
{
FILEIO_BACKUP_INFO_ENTRY *temp_entry_p;
FILEIO_BACKUP_INFO_QUEUE *data_p;
data_p = &(fileio_Backup_vol_info_data[which_bkvinf]);
if (!(*data_p).initialized)
{
return NULL;
}
/* check free list */
if ((*data_p).free != NULL)
{
temp_entry_p = (*data_p).free;
(*data_p).free = temp_entry_p->link;
temp_entry_p->link = NULL;
}
else
{
/* allocate one */
temp_entry_p = ((FILEIO_BACKUP_INFO_ENTRY *) malloc (sizeof (FILEIO_BACKUP_INFO_ENTRY)));
if (temp_entry_p != NULL)
{
temp_entry_p->link = NULL;
}
else
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (FILEIO_BACKUP_INFO_ENTRY));
}
}
return temp_entry_p;
}
/*
* fileio_clear_backup_info_level () - REMOVES ALL VOLUMES OF A BACKUP LEVEL FROM CACHE
* return:
* level(in): backup level to remove
* dealloc(in): true if the nodes should be freed back to the OS
* which_bkvinf(in):
*
* Note: This routine removes all the nodes for a given level and any levels
* above it. Ordinarily the nodes will be placed on the free list for
* reuse, but if dealloc is true, then the space will be freed.
* Example: IF level 1 is to be cleared, then level 2 is also cleared.
*/
int
fileio_clear_backup_info_level (int level, bool is_dealloc, int which_bkvinf)
{
FILEIO_BACKUP_INFO_ENTRY *next_p;
FILEIO_BACKUP_INFO_QUEUE *data_p;
int i;
data_p = &(fileio_Backup_vol_info_data[which_bkvinf]);
if (level < 0)
{
level = FILEIO_BACKUP_FULL_LEVEL;
}
/* Delete from the front */
for (i = FILEIO_BACKUP_UNDEFINED_LEVEL - 1; i >= level; i--)
{
while ((*data_p).anchors[i] != NULL)
{
next_p = (*data_p).anchors[i]->link;
if (is_dealloc)
{
free_and_init ((*data_p).anchors[i]);
}
else
{
/* stick it on the free list */
(*data_p).anchors[i]->link = (*data_p).free;
(*data_p).free = (*data_p).anchors[i];
}
(*data_p).anchors[i] = next_p;
}
}
/* Free the free list nodes if necessary to avoid leaks */
if (is_dealloc)
{
while ((*data_p).free)
{
next_p = (*data_p).free->link;
free_and_init ((*data_p).free);
(*data_p).free = next_p;
}
}
return NO_ERROR;
}
/*
* fileio_request_user_response () - REQUEST A RESPONSE VIA REMOVE CLIENT
* return:
* prompt_id(in):
* prompt(in):
* response(in):
* failure_prompt(in):
* range_low(in):
* range_high(in):
* secondary_prompt(in):
* reprompt_value(in):
*/
int
fileio_request_user_response (THREAD_ENTRY * thread_p, FILEIO_REMOTE_PROMPT_TYPE prompt_id, const char *prompt_p,
char *response_p, const char *failure_prompt_p, int range_low, int range_high,
const char *secondary_prompt_p, int reprompt_value)
{
#if defined(SERVER_MODE)
char *remote_data_p = NULL;
char *remote_answer_p = NULL;
int data_size;
int remote_status;
char *ptr;
/* Send the prompt to the client */
if (xio_send_user_prompt_to_client (thread_p, prompt_id, prompt_p, failure_prompt_p, range_low, range_high,
secondary_prompt_p, reprompt_value) != NO_ERROR)
{
return ER_FAILED;
}
/* Obtain the user's response from the client, without blocking the server. */
if (xs_receive_data_from_client (thread_p, &remote_data_p, &data_size) != NO_ERROR)
{
if (remote_data_p)
{
thread_p->release_packet (remote_data_p);
}
return ER_FAILED;
}
ptr = or_unpack_int (remote_data_p, &remote_status);
if (remote_status != NO_ERROR)
{
thread_p->release_packet (remote_data_p);
return ER_FAILED;
}
data_size -= OR_INT_SIZE;
if (response_p && data_size > 0)
{
/* Otherwise prompt appears successful */
ptr = or_unpack_string_nocopy (ptr, &remote_answer_p);
if (remote_answer_p != NULL)
{
memcpy (response_p, remote_answer_p, intl_mbs_len (remote_answer_p) + 1);
}
}
thread_p->release_packet (remote_data_p);
return NO_ERROR;
#else /* SERVER_MODE */
extern unsigned int db_on_server;
char new_vol_name[FILEIO_MAX_USER_RESPONSE_SIZE];
char *user_response_p = new_vol_name;
const char *display_string_p;
char line_buf[PATH_MAX * 2];
int pr_status, pr_len;
int x;
int result = 0;
bool is_retry_in = true;
int rc;
char format_string[32];
/* we're pretending to jump to the client */
db_on_server = 0;
/* Interestingly enough, this is basically the same code as in the ASYNC_ callback has to do remotely. */
display_string_p = prompt_p;
memset (new_vol_name, 0, sizeof (new_vol_name));
sprintf (format_string, "%%%ds", FILEIO_MAX_USER_RESPONSE_SIZE - 1);
while (is_retry_in)
{
/* Display prompt, then get user's input. */
fprintf (stdout, display_string_p);
pr_status = ER_FAILED;
pr_len = 0;
is_retry_in = false;
if (prompt_id != FILEIO_PROMPT_DISPLAY_ONLY)
{
rc = -1;
if ((fgets (line_buf, PATH_MAX, stdin) != NULL)
&& ((rc = sscanf (line_buf, format_string, user_response_p)) > 0))
{
/* Attempt basic input int validation before we send it back */
switch (prompt_id)
{
case FILEIO_PROMPT_RANGE_TYPE:
/* Numeric range checking */
result = parse_int (&x, user_response_p, 10);
if (result != 0 || x < range_low || x > range_high)
{
fprintf (stdout, failure_prompt_p);
is_retry_in = true;
}
else
{
pr_status = NO_ERROR;
}
break;
/* attempt simply boolean (y, yes, 1, n, no, 0) validation */
case FILEIO_PROMPT_BOOLEAN_TYPE:
if (char_tolower (*user_response_p) == 'y' || *user_response_p == '1'
|| intl_mbs_casecmp ((const char *) user_response_p, "yes") == 0)
{
pr_status = NO_ERROR;
/* convert all affirmate answers into '1' */
strcpy (user_response_p, "1");
}
else
{
pr_status = NO_ERROR;
/* convert all negative answers into '0' */
strcpy (user_response_p, "0");
}
break;
/* no validation to do */
case FILEIO_PROMPT_STRING_TYPE:
pr_status = NO_ERROR;
break;
/* Validate initial prompt, then post secondary prompt */
case FILEIO_PROMPT_RANGE_WITH_SECONDARY_STRING_TYPE:
/* Numeric range checking on the first promp, but user's answer we really want is the second prompt */
result = parse_int (&x, user_response_p, 10);
if (result != 0 || x < range_low || x > range_high)
{
fprintf (stdout, failure_prompt_p);
is_retry_in = true;
}
else if (x == reprompt_value)
{
/* The first answer requires another prompt */
display_string_p = secondary_prompt_p;
is_retry_in = true;
prompt_id = FILEIO_PROMPT_STRING_TYPE;
/* moving the response buffer ptr forward insures that both the first response and the second are
* included in the buffer. (no delimiter or null bytes) */
user_response_p += intl_mbs_len (user_response_p);
}
else
{
/* This answer was sufficient */
pr_status = NO_ERROR;
}
break;
default:
/* should we treat this as an error? It is really a protocol error. How do we handle backward
* compatibility for future releases? */
pr_status = NO_ERROR;
}
}
else if (rc == 0)
{
is_retry_in = true;
}
else
{
/* EOF encountered, treat as an error */
return ER_FAILED;
}
}
}
/* The answer can be returned now. It should be stored in new_vol_name */
/* check for overflow, could be dangerous */
pr_len = intl_mbs_len (new_vol_name);
if (pr_len > FILEIO_MAX_USER_RESPONSE_SIZE)
{
pr_status = ER_FAILED;
er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_NET_DATA_TRUNCATED, 0);
}
/* Copy the answer to the response buffer */
if (response_p)
{
memcpy (response_p, new_vol_name, sizeof (new_vol_name));
}
db_on_server = 1;
return (pr_status);
#endif /* SERVER_MODE */
}
#if !defined(WINDOWS)
/*
* fileio_symlink () -
* return:
* src(in):
* dest(in):
* overwrite(in):
*/
int
fileio_symlink (const char *src_p, const char *dest_p, int overwrite)
{
if (overwrite && fileio_is_volume_exist (dest_p))
{
unlink (dest_p);
}
if (symlink (src_p, dest_p) == -1)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_BO_CANNOT_CREATE_LINK, 2, src_p, dest_p);
return ER_FAILED;
}
return NO_ERROR;
}
/*
* fileio_lock_region () -
* return:
* fd(in):
* cmd(in):
* type(in):
* offset(in):
* whence(in):
* len(in):
*/
static int
fileio_lock_region (int fd, int cmd, int type, off_t offset, int whence, off_t len)
{
struct flock lock;
lock.l_type = type; /* F_RDLOCK, F_WRLOCK, F_UNLOCK */
lock.l_start = offset; /* byte offset, relative to l_whence */
lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
lock.l_len = len; /* #bytes (O means to EOF) */
return fcntl (fd, cmd, &lock);
}
#endif /* !WINDOWS */
#if defined(ENABLE_UNUSED_FUNCTION)
#if defined(SERVER_MODE)
/*
* fileio_os_sysconf () -
* return:
*/
int
fileio_os_sysconf (void)
{
long nprocs = -1;
#if defined(_SC_NPROCESSORS_ONLN)
nprocs = sysconf (_SC_NPROCESSORS_ONLN);
#elif defined(_SC_NPROC_ONLN)
nprocs = sysconf (_SC_NPROC_ONLN);
#elif defined(_SC_CRAY_NCPU)
nprocs = sysconf (_SC_CRAY_NCPU);
#elif defined(WINDOWS)
{
SYSTEM_INFO sysinfo;
/* determine the base of virtual memory */
GetSystemInfo (&sysinfo);
nprocs = sysinfo.dwNumberOfProcessors;
}
#else /* WINDOWS */
; /* give up */
#endif /* WINDOWS */
return (nprocs > 1) ? (int) nprocs : 1;
}
#endif /* SERVER_MODE */
#endif
/*
* fileio_initialize_res () -
* return:
*/
void
fileio_initialize_res (THREAD_ENTRY * thread_p, FILEIO_PAGE * io_page, PGLENGTH page_size)
{
fileio_init_lsa_of_page (io_page, page_size);
io_page->prv.pageid = -1;
io_page->prv.volid = -1;
io_page->prv.ptype = '\0';
io_page->prv.pflag = '\0';
io_page->prv.p_reserve_1 = 0;
io_page->prv.p_reserve_2 = 0;
io_page->prv.tde_nonce = 0;
}
/*
* PAGE BITMAP FUNCTIONS
*/
/*
* fileio_page_bitmap_list_init - initialize a page bitmap list
* return: void
* page_bitmap_list(in/out): head of the page bitmap list
*/
void
fileio_page_bitmap_list_init (FILEIO_RESTORE_PAGE_BITMAP_LIST * page_bitmap_list)
{
assert (page_bitmap_list != NULL);
page_bitmap_list->head = NULL;
page_bitmap_list->tail = NULL;
}
/*
* fileio_page_bitmap_create - create a page bitmap
* return: page bitmap
* vol_id(in): the number of the page bitmap identification
* total_pages(in): the number of total pages
*/
FILEIO_RESTORE_PAGE_BITMAP *
fileio_page_bitmap_create (int vol_id, int total_pages)
{
FILEIO_RESTORE_PAGE_BITMAP *page_bitmap;
int page_bitmap_size;
page_bitmap = (FILEIO_RESTORE_PAGE_BITMAP *) malloc (sizeof (FILEIO_RESTORE_PAGE_BITMAP));
if (page_bitmap == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (FILEIO_RESTORE_PAGE_BITMAP));
return NULL;
}
page_bitmap_size = CEIL_PTVDIV (total_pages, 8);
page_bitmap->next = NULL;
page_bitmap->vol_id = vol_id;
page_bitmap->size = page_bitmap_size;
page_bitmap->bitmap = (unsigned char *) malloc (page_bitmap_size);
if (page_bitmap->bitmap == NULL)
{
free_and_init (page_bitmap);
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (size_t) page_bitmap_size);
return NULL;
}
memset (page_bitmap->bitmap, 0x0, page_bitmap_size);
return page_bitmap;
}
/*
* fileio_page_bitmap_list_find - find the page bitmap which is matched
* with vol_id
* return: pointer of the page bitmap or NULL
* page_bitmap_list(in): head of the page bitmap list
* vol_id(in): the number of the page bitmap identification
*/
FILEIO_RESTORE_PAGE_BITMAP *
fileio_page_bitmap_list_find (FILEIO_RESTORE_PAGE_BITMAP_LIST * page_bitmap_list, int vol_id)
{
FILEIO_RESTORE_PAGE_BITMAP *page_bitmap;
if (page_bitmap_list->head == NULL)
{
return NULL;
}
assert (page_bitmap_list->tail != NULL);
page_bitmap = page_bitmap_list->head;
while (page_bitmap != NULL)
{
if (page_bitmap->vol_id == vol_id)
{
return page_bitmap;
}
page_bitmap = page_bitmap->next;
}
return NULL;
}
/*
* fileio_page_bitmap_list_add - add a page bitmap to a page bitmap list
* page_bitmap_list(in/out): head of the page bitmap list
* page_bitmap(in): pointer of the page bitmap
*/
void
fileio_page_bitmap_list_add (FILEIO_RESTORE_PAGE_BITMAP_LIST * page_bitmap_list,
FILEIO_RESTORE_PAGE_BITMAP * page_bitmap)
{
#if !defined(NDEBUG)
FILEIO_RESTORE_PAGE_BITMAP *bitmap;
#endif
assert (page_bitmap_list != NULL);
assert (page_bitmap != NULL);
#if !defined(NDEBUG)
/* Check the uniqueness of vol_id */
bitmap = page_bitmap_list->head;
while (bitmap != NULL)
{
assert (bitmap->vol_id != page_bitmap->vol_id);
bitmap = bitmap->next;
}
#endif
if (page_bitmap_list->head == NULL)
{
assert (page_bitmap_list->tail == NULL);
page_bitmap_list->head = page_bitmap;
page_bitmap_list->tail = page_bitmap;
}
else
{
assert (page_bitmap_list->tail != NULL);
page_bitmap_list->tail->next = page_bitmap;
page_bitmap_list->tail = page_bitmap;
}
}
/*
* fileio_page_bitmap_list_destroy - destroy a page bitmap list
* return: void
* page_bitmap_list(in/out): head of the page bitmap list
*/
void
fileio_page_bitmap_list_destroy (FILEIO_RESTORE_PAGE_BITMAP_LIST * page_bitmap_list)
{
FILEIO_RESTORE_PAGE_BITMAP *page_bitmap = NULL;
FILEIO_RESTORE_PAGE_BITMAP *page_bitmap_next = NULL;
assert (page_bitmap_list != NULL);
page_bitmap = page_bitmap_list->head;
while (page_bitmap != NULL)
{
page_bitmap_next = page_bitmap->next;
page_bitmap->vol_id = 0;
page_bitmap->size = 0;
free_and_init (page_bitmap->bitmap);
free_and_init (page_bitmap);
page_bitmap = page_bitmap_next;
}
page_bitmap_list->head = NULL;
page_bitmap_list->tail = NULL;
}
/*
* fileio_page_bitmap_set - set the bit that represents the exitence of the page
* return: void
* page_bitmap(in): pointer of the page bitmap
* page_id(in): position of the page
*/
static void
fileio_page_bitmap_set (FILEIO_RESTORE_PAGE_BITMAP * page_bitmap, int page_id)
{
assert (page_bitmap != NULL);
assert ((page_bitmap->size - 1) >= (page_id / 8));
page_bitmap->bitmap[page_id / 8] |= 1 << (page_id % 8);
}
/*
* fileio_page_bitmap_is_set - get the bit that represents the exitence of the page
* return: if the bit of page is set then it returns true.
* page_bitmap(in): pointer of the page bitmap
* page_id(in): position of the page
*/
static bool
fileio_page_bitmap_is_set (FILEIO_RESTORE_PAGE_BITMAP * page_bitmap, int page_id)
{
bool is_set;
assert (page_bitmap != NULL);
assert ((page_bitmap->size - 1) >= (page_id / 8));
is_set = page_bitmap->bitmap[page_id / 8] & (1 << (page_id % 8)) ? true : false;
return is_set;
}
/*
* fileio_page_bitmap_dump - dump a page bitmap
* return: void
* out_fp(in): FILE stream where to dump; if NULL, stdout
* page_bitmap(in): pointer of the page bitmap
*/
static void
fileio_page_bitmap_dump (FILE * out_fp, const FILEIO_RESTORE_PAGE_BITMAP * page_bitmap)
{
int i;
assert (page_bitmap != NULL);
if (out_fp == NULL)
{
out_fp = stdout;
}
fprintf (out_fp, "BITMAP_ID = %d, BITMAP_SIZE = %d\n", page_bitmap->vol_id, page_bitmap->size);
for (i = 0; i < page_bitmap->size; i++)
{
if ((i % 32) == 0)
{
fprintf (out_fp, "%#08X: ", i);
}
else
{
fprintf (out_fp, "%02X ", page_bitmap->bitmap[i]);
}
if ((i % 32) == 31)
{
fprintf (out_fp, "\n");
}
}
fprintf (out_fp, "\n");
}
/*
* fileio_page_check_corruption - Check whether the page is corrupted.
* return: error code
* thread_p (in): thread entry
* io_page (in): the page
* is_page_corrupted (out): true, if the page is corrupted.
*/
int
fileio_page_check_corruption (THREAD_ENTRY * thread_p, FILEIO_PAGE * io_page, bool * is_page_corrupted)
{
assert (io_page != NULL && is_page_corrupted != NULL);
*is_page_corrupted = !fileio_is_page_sane (io_page, IO_PAGESIZE);
return NO_ERROR;
}
bool
fileio_is_formatted_page (THREAD_ENTRY * thread_p, const char *io_page)
{
char *ref_page;
bool is_formatted = false;
ref_page = (char *) malloc (IO_PAGESIZE);
if (ref_page == NULL)
{
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, IO_PAGESIZE);
return false;
}
memset ((char *) ref_page, 0, IO_PAGESIZE);
(void) fileio_initialize_res (thread_p, (FILEIO_PAGE *) ref_page, IO_PAGESIZE);
if (memcmp (io_page, ref_page, IO_PAGESIZE) == 0)
{
is_formatted = true;
}
free_and_init (ref_page);
return is_formatted;
}
/*
* fileio_lob_remove_dir () - Remove LOB directory(OS level).
*
* return: error code.
* path (in): An absolute path under the LOB directory.
*/
int
fileio_lob_remove_dir (char *path)
{
#if defined(SERVER_MODE) || defined(SA_MODE)
DIR *dir_p;
struct dirent *dir_entry;
struct stat statbuf;
char sub_path[PATH_MAX];
int result = 0;
dir_p = opendir (path);
if (dir_p == NULL)
{
return ER_ES_INVALID_PATH;
}
while ((dir_entry = readdir (dir_p)) != NULL && result == 0)
{
if (strcmp (dir_entry->d_name, ".") == 0 || strcmp (dir_entry->d_name, "..") == 0)
{
continue;
}
#if defined (__GNUC__)
DISABLE_FMT_TRUNC_WARNING
#endif
snprintf (sub_path, PATH_MAX, "%s%c%s", path, PATH_SEPARATOR, dir_entry->d_name);
#if defined (__GNUC__)
ENABLE_FMT_TRUNC_WARNING
#endif
if (stat (sub_path, &statbuf) != 0)
{
continue;
}
if (S_ISDIR (statbuf.st_mode))
{
result = fileio_lob_remove_dir (sub_path);
}
else
{
result = unlink (sub_path);
}
}
if (result != 0)
{
return ER_FAILED;
}
result = closedir (dir_p);
if (result != 0)
{
return ER_FAILED;
}
result = rmdir (path);
if (result != 0)
{
return ER_FAILED;
}
return NO_ERROR;
#else /* SERVER_MODE || SA_MODE */
return ER_FAILED; /* Not supported in CS_MODE because it handles server-side external storage. */
#endif /* SERVER_MODE || SA_MODE */
}
/*
* fileio_lob_remove_matching_dir () - Remove LOB subdirectories whose names contain the given keyword by calling
* fileio_lob_remove_dir().
*
* return: error code.
* keyword (in): keyword (in): A keyword representing part or all of a directory name
* to be removed under the LOB path.
* Examples include an HFID (attrid) or a temporary directory name ("ces").
*/
int
fileio_lob_remove_matching_dir (const char *keyword)
{
#if defined(SERVER_MODE) || defined(SA_MODE)
DIR *dir_p;
struct dirent *dir_entry;
struct stat statbuf;
char sub_path[PATH_MAX];
int result = 0;
dir_p = opendir (es_base_dir);
if (dir_p == NULL)
{
/*
* If opendir() failed (e.g., when es_base_dir is an invalid path),
* ignore the error in order to start the server nomally.
* This approach follows the design of es_init().
*/
return NO_ERROR;
}
while ((dir_entry = readdir (dir_p)) != NULL && result == 0)
{
if (strcmp (dir_entry->d_name, ".") == 0 || strcmp (dir_entry->d_name, "..") == 0)
{
continue;
}
if (strncmp (dir_entry->d_name, keyword, strlen (keyword)) == 0)
{
#if defined (__GNUC__)
DISABLE_FMT_TRUNC_WARNING
#endif
snprintf (sub_path, PATH_MAX, "%s%c%s", es_base_dir, PATH_SEPARATOR, dir_entry->d_name);
#if defined (__GNUC__)
ENABLE_FMT_TRUNC_WARNING
#endif
if (stat (sub_path, &statbuf) != 0)
{
continue;
}
if (S_ISDIR (statbuf.st_mode))
{
result = fileio_lob_remove_dir (sub_path);
}
else
{
/* This case should never happen. */
}
}
}
if (result != 0)
{
return ER_FAILED;
}
result = closedir (dir_p);
if (result != 0)
{
return ER_FAILED;
}
return NO_ERROR;
#else /* SERVER_MODE || SA_MODE */
return ER_FAILED; /* Not supported in CS_MODE because it handles server-side external storage. */
#endif /* SERVER_MODE || SA_MODE */
}