mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-27 20:03:03 +01:00
1461 lines
32 KiB
C++
1461 lines
32 KiB
C++
/*
|
|
* PROGRAM: JRD Access Method
|
|
* MODULE: jio.c
|
|
* DESCRIPTION: Journal file i/o
|
|
*
|
|
* The contents of this file are subject to the Interbase Public
|
|
* License Version 1.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.Inprise.com/IPL.html
|
|
*
|
|
* Software distributed under the License is distributed on an
|
|
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
|
|
* or implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code was created by Inprise Corporation
|
|
* and its predecessors. Portions created by Inprise Corporation are
|
|
* Copyright (C) Inprise Corporation.
|
|
*
|
|
* All Rights Reserved.
|
|
* Contributor(s): ______________________________________.
|
|
*
|
|
* 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "EPSON" define
|
|
* 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "DELTA" port
|
|
* 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "M88K" port
|
|
*
|
|
*/
|
|
|
|
/* NOTE that this module is used only on platforms that use
|
|
VMS style journaling. At the moment this is limited to
|
|
VMS and MPE XL. */
|
|
|
|
#include "firebird.h"
|
|
#include "../jrd/ib_stdio.h"
|
|
#include "../jrd/jrd.h"
|
|
#include "../jrd/jrn.h"
|
|
#include "gen/codes.h"
|
|
|
|
#ifdef VMS
|
|
#include <file.h>
|
|
#include <lckdef.h>
|
|
#include "../jrd/vms.h"
|
|
#define EVENT_FLAG 15
|
|
#define SYS_ERROR gds_arg_vms
|
|
#endif
|
|
|
|
#ifndef VMS
|
|
#include <sys/types.h>
|
|
|
|
#ifdef SOLARIS
|
|
#include <fcntl.h>
|
|
#endif
|
|
|
|
#ifdef SCO_UNIX
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
#ifdef PC_PLATFORM
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <io.h>
|
|
#define FILE_OPEN(name,access,mask) open (name, access)
|
|
#else
|
|
#include <sys/file.h>
|
|
#endif
|
|
|
|
#ifdef NETWARE_386
|
|
#include <errno.h>
|
|
#endif
|
|
|
|
#define SYS_ERROR gds_arg_unix
|
|
extern int errno;
|
|
#endif
|
|
|
|
#ifndef FILE_OPEN
|
|
#define FILE_OPEN(name,access,mask) open (name, access, mask)
|
|
#endif
|
|
|
|
#ifndef O_BINARY
|
|
#define O_BINARY 0
|
|
#endif
|
|
|
|
#define BLOCKS(length) ((length + BLOCK_SIZE - 1) / BLOCK_SIZE)
|
|
|
|
extern SLONG LOCK_enq();
|
|
extern SLONG LOCK_read_data();
|
|
DIR JIO_next_dir();
|
|
SLONG JIO_rewind();
|
|
static SLONG add_database();
|
|
static SLONG block_number();
|
|
static DIR locate_dir();
|
|
static SLONG lookup_id();
|
|
|
|
/* Lock Block data */
|
|
|
|
typedef union {
|
|
struct {
|
|
SSHORT segment;
|
|
SSHORT block;
|
|
} data_items;
|
|
SLONG data_long;
|
|
} LDATA;
|
|
|
|
|
|
JIO_fini(journal)
|
|
JRN journal;
|
|
{
|
|
/**************************************
|
|
*
|
|
* J I O _ f i n i
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Signoff journal file.
|
|
*
|
|
**************************************/
|
|
LDATA data;
|
|
struct jfh header;
|
|
int status;
|
|
|
|
acquire(journal, &data);
|
|
position_and_read(journal, 0L, &header, sizeof(header));
|
|
header.jfh_write_block = data.data_items.block;
|
|
position_and_write(journal, 0L, &header, sizeof(header));
|
|
|
|
lock_release(journal);
|
|
|
|
#ifdef VMS
|
|
status = sys$dassgn(journal->jrn_channel);
|
|
|
|
if (!(status & 1))
|
|
error(journal, status, "sys$dassgn", isc_io_access_err);
|
|
|
|
#endif
|
|
|
|
#ifndef VMS
|
|
close((int) journal->jrn_channel);
|
|
#endif
|
|
}
|
|
|
|
|
|
JIO_get_position(journal, data)
|
|
JRN journal;
|
|
LDATA *data;
|
|
{
|
|
/**************************************
|
|
*
|
|
* J I O _ g e t _ p o s i t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get current output position in journal file. This is
|
|
* used to implement FLUSH verb in journal server.
|
|
*
|
|
**************************************/
|
|
|
|
acquire(journal, data);
|
|
release(journal, data);
|
|
}
|
|
|
|
|
|
DIR JIO_next_dir(dir)
|
|
DIR dir;
|
|
{
|
|
/**************************************
|
|
*
|
|
* J I O _ n e x t _ d i r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Compute address of next directory entry.
|
|
*
|
|
**************************************/
|
|
USHORT length;
|
|
|
|
length = sizeof(struct dir) - 1 + dir->dir_length;
|
|
length = (length + 1) & ~1;
|
|
|
|
return (DIR) ((TEXT *) dir + length);
|
|
}
|
|
|
|
|
|
JIO_open(journal)
|
|
JRN journal;
|
|
{
|
|
/**************************************
|
|
*
|
|
* J I O _ o p e n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
**************************************/
|
|
TEXT *file_name;
|
|
USHORT new;
|
|
LDATA data;
|
|
struct jfh header;
|
|
int n, mask;
|
|
|
|
/* Try to open the journal file. If we can't, try to create it. If we
|
|
create it, format it. If we can't open or create, try to open one
|
|
last time. */
|
|
|
|
if (new = journal_open(journal, &data)) {
|
|
clear(&header, sizeof(header));
|
|
header.jfh_version = JFH_VERSION;
|
|
header.jfh_free = SEGMENT_SLOTS;
|
|
n = header.jfh_segments[0] = header.jfh_allocation++;
|
|
position_and_write(journal, 0L, &header, sizeof(header));
|
|
}
|
|
|
|
/* Get intermediate file header */
|
|
|
|
position_and_read(journal, 0L, &header, sizeof(header));
|
|
|
|
if (header.jfh_version != JFH_VERSION) {
|
|
ERR_post(gds__jrn_format_err, 0);
|
|
/* Msg366 journal file wrong format */
|
|
}
|
|
|
|
/* Try to get exclusive lock. If so, do a little cleanup */
|
|
|
|
if (lock_exclusive(journal, FALSE)) {
|
|
data.data_items.segment = header.jfh_segments[header.jfh_data];
|
|
if (!new)
|
|
header.jfh_write_block = high_water(journal, &header);
|
|
data.data_items.block = header.jfh_write_block;
|
|
++header.jfh_series;
|
|
position_and_write(journal, 0L, &header, sizeof(header));
|
|
}
|
|
|
|
journal->jrn_series = header.jfh_series;
|
|
release(journal, &data);
|
|
}
|
|
|
|
|
|
JIO_put(journal, message, size)
|
|
JRN journal;
|
|
LTJC *message;
|
|
USHORT size;
|
|
{
|
|
/**************************************
|
|
*
|
|
* J I O _ p u t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Write a record to the journal file.
|
|
*
|
|
**************************************/
|
|
SLONG block, blocks;
|
|
SSHORT n, fudge;
|
|
struct jrnh dummy;
|
|
LDATA data;
|
|
USHORT waited;
|
|
|
|
/* Get write lock on journal file. The lock block will tell us where
|
|
to write next */
|
|
|
|
acquire(journal, &data);
|
|
|
|
/* Do any message processing necessary */
|
|
|
|
switch (message->ltjc_header.jrnh_type) {
|
|
case JRN_ENABLE:
|
|
journal->jrn_handle = add_database(journal,
|
|
message->ltjc_data,
|
|
message->ltjc_length);
|
|
message->ltjc_header.jrnh_handle = journal->jrn_handle;
|
|
message->ltjc_header.jrnh_series = journal->jrn_series;
|
|
break;
|
|
|
|
case JRN_SIGN_ON:
|
|
journal->jrn_handle = lookup_id(journal,
|
|
message->ltjc_data,
|
|
message->ltjc_length);
|
|
if (!journal->jrn_handle) {
|
|
journal->jrn_handle = add_database(journal,
|
|
message->ltjc_data,
|
|
message->ltjc_length);
|
|
journal->jrn_dump = -1;
|
|
}
|
|
message->ltjc_header.jrnh_handle = journal->jrn_handle;
|
|
message->ltjc_header.jrnh_series = journal->jrn_series;
|
|
break;
|
|
}
|
|
|
|
/* Compute next available block. If there isn't room in this segment, write a
|
|
dummy block to take the extra space, then allocate new data segment. */
|
|
|
|
block = block_number(data.data_items.segment, data.data_items.block);
|
|
blocks = (SLONG) (BLOCKS(size));
|
|
|
|
if (data.data_items.block + (SSHORT) blocks > SEGMENT_SIZE) {
|
|
if (fudge = SEGMENT_SIZE - data.data_items.block) {
|
|
dummy.jrnh_type = JRN_DUMMY;
|
|
dummy.jrnh_length = BLOCK_SIZE * fudge;
|
|
dummy.jrnh_handle = journal->jrn_handle;
|
|
dummy.jrnh_series = journal->jrn_series;
|
|
position_and_write(journal, block, &dummy, sizeof(struct jrnh));
|
|
}
|
|
|
|
waited = 0;
|
|
while ((n = extend_segments(journal)) == -1 && waited < 300) {
|
|
release(journal, &data);
|
|
waited += 10;
|
|
sleep(10);
|
|
acquire(journal, &data);
|
|
}
|
|
if (n == -1)
|
|
ERR_post(gds__jrn_file_full, 0);
|
|
/* Msg840 intermediate journal file full */
|
|
data.data_items.segment = n;
|
|
data.data_items.block = 0;
|
|
block = block_number(data.data_items.segment, data.data_items.block);
|
|
}
|
|
|
|
n = (size + 1) & ~1;
|
|
position_and_write(journal, block, message, n);
|
|
data.data_items.block += (SSHORT) blocks;
|
|
release(journal, &data);
|
|
}
|
|
|
|
|
|
JIO_read(journal, position, buffer, size)
|
|
JRN journal;
|
|
USHORT *position;
|
|
JRNH *buffer;
|
|
USHORT size;
|
|
{
|
|
/**************************************
|
|
*
|
|
* J I O _ r e a d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Read a record from the journal file. Returns the number of bytes read.
|
|
*
|
|
**************************************/
|
|
USHORT length, n, *ptr;
|
|
SLONG block;
|
|
struct jfh header;
|
|
LDATA data;
|
|
|
|
/* Acquire lock file */
|
|
|
|
acquire(journal, &data);
|
|
|
|
/* Make sure we don't pass writers */
|
|
|
|
for (;;) {
|
|
if (position[0] == data.data_items.segment &&
|
|
position[1] >= data.data_items.block) {
|
|
release(journal, &data);
|
|
return 0;
|
|
}
|
|
if (position[1] < SEGMENT_SIZE)
|
|
break;
|
|
position_and_read(journal, 0L, &header, sizeof(header));
|
|
for (ptr = header.jfh_segments; *ptr != position[0]; ++ptr);
|
|
position[0] = ptr[1];
|
|
position[1] = 0;
|
|
}
|
|
|
|
/* Read first block of record. This may be too much or too little,
|
|
so we may have to read more */
|
|
|
|
block = block_number(position[0], position[1]);
|
|
position_and_read(journal, block, buffer, BLOCK_SIZE);
|
|
length = buffer->jrnh_length;
|
|
|
|
if (length > BLOCK_SIZE) {
|
|
n = length - BLOCK_SIZE;
|
|
n = (n + 1) & ~1;
|
|
position_and_read(journal, block + 1, (UCHAR *) buffer + BLOCK_SIZE,
|
|
n);
|
|
}
|
|
|
|
/* Update cursor */
|
|
|
|
position[1] += BLOCKS(length);
|
|
|
|
/* Release file and return */
|
|
|
|
release(journal, &data);
|
|
|
|
return length;
|
|
}
|
|
|
|
|
|
JIO_read_header(journal, header, h_length, id_space, i_length)
|
|
JRN journal;
|
|
JFH header;
|
|
USHORT h_length;
|
|
TEXT *id_space;
|
|
USHORT i_length;
|
|
{
|
|
/**************************************
|
|
*
|
|
* J I O _ r e a d _ h e a d e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Read header data. If lengths are wrong, return FALSE and
|
|
* give up, otherwise return TRUE.
|
|
*
|
|
**************************************/
|
|
LDATA data;
|
|
USHORT n;
|
|
|
|
if (h_length != sizeof(struct jfh))
|
|
return FALSE;
|
|
|
|
acquire(journal, &data);
|
|
position_and_read(journal, 0L, header, h_length);
|
|
|
|
if (id_space)
|
|
if (n = MIN(i_length, header->jfh_id_space))
|
|
position_and_read(journal, (SLONG) ID_OFFSET, id_space, n);
|
|
|
|
release(journal, &data);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
SLONG JIO_rewind(journal, position)
|
|
JRN journal;
|
|
USHORT *position;
|
|
{
|
|
/**************************************
|
|
*
|
|
* J I O _ r e w i n d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Prepare to read from journal. Return current
|
|
* sequence number.
|
|
*
|
|
**************************************/
|
|
USHORT block, length, n, *ptr;
|
|
struct jfh header;
|
|
LDATA data;
|
|
SLONG sequence;
|
|
|
|
/* Acquire lock file */
|
|
|
|
acquire(journal, &data);
|
|
position_and_read(journal, 0L, &header, sizeof(header));
|
|
position[0] = header.jfh_segments[0];
|
|
position[1] = header.jfh_copy_block;
|
|
sequence = header.jfh_sequence;
|
|
release(journal, &data);
|
|
|
|
return sequence;
|
|
}
|
|
|
|
|
|
JIO_truncate(journal, position, sequence)
|
|
JRN journal;
|
|
USHORT *position;
|
|
SLONG sequence;
|
|
{
|
|
/**************************************
|
|
*
|
|
* J I O _ t r u n c a t e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Release any data pages up to a given point.
|
|
*
|
|
**************************************/
|
|
USHORT n, length, *ptr, *ptr2, slots[SEGMENT_SLOTS];
|
|
struct jfh header;
|
|
LDATA data;
|
|
|
|
/* Acquire lock file */
|
|
|
|
acquire(journal, &data);
|
|
position_and_read(journal, 0L, &header, sizeof(header));
|
|
|
|
for (ptr = header.jfh_segments, ptr2 = slots; *ptr != position[0];)
|
|
*ptr2++ = *ptr++;
|
|
|
|
if (n = ptr2 - slots) {
|
|
header.jfh_data -= n;
|
|
length = (header.jfh_data + 1) * sizeof(USHORT);
|
|
move(ptr, header.jfh_segments, length);
|
|
header.jfh_free -= n;
|
|
move(slots, header.jfh_segments + header.jfh_free,
|
|
n * sizeof(USHORT));
|
|
}
|
|
|
|
header.jfh_copy_block = position[1];
|
|
header.jfh_sequence = sequence;
|
|
position_and_write(journal, 0L, &header, sizeof(header));
|
|
|
|
release(journal, &data);
|
|
}
|
|
|
|
|
|
static acquire(journal, data)
|
|
JRN journal;
|
|
LDATA *data;
|
|
{
|
|
/**************************************
|
|
*
|
|
* a c q u i r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Acquire write lock on journal file, getting lock data block.
|
|
*
|
|
**************************************/
|
|
|
|
#ifdef VMS
|
|
vms_convert(journal, data, LCK$K_PWMODE, TRUE);
|
|
#endif
|
|
|
|
#ifndef VMS
|
|
LOCK_convert(journal->jrn_lock_id, LCK_PW, TRUE, 0, 0,
|
|
journal->jrn_status_vector);
|
|
data->data_long = LOCK_read_data(journal->jrn_lock_id);
|
|
#endif
|
|
}
|
|
|
|
|
|
static SLONG add_database(journal, name_string, name_length)
|
|
JRN journal;
|
|
TEXT *name_string;
|
|
USHORT name_length;
|
|
{
|
|
/**************************************
|
|
*
|
|
* a d d _ d a t a b a s e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Add a new database to journal file. Return new id.
|
|
*
|
|
**************************************/
|
|
TEXT id_space[ID_SPACE];
|
|
DIR dir, next;
|
|
struct jfh header;
|
|
ULONG id;
|
|
|
|
position_and_read(journal, 0L, &header, sizeof(header));
|
|
position_and_read(journal, (SLONG) ID_OFFSET, id_space,
|
|
header.jfh_id_space);
|
|
delete_dir(&header, id_space, name_string, name_length);
|
|
dir = (DIR) (id_space + header.jfh_id_space);
|
|
|
|
dir->dir_id = id = ++header.jfh_next_id;
|
|
dir->dir_length = name_length;
|
|
strncpy(dir->dir_name, name_string, name_length);
|
|
dir->dir_last_page = 0;
|
|
|
|
next = JIO_next_dir(dir);
|
|
header.jfh_id_space = (TEXT *) next - id_space;
|
|
position_and_write(journal, (SLONG) ID_OFFSET, id_space,
|
|
header.jfh_id_space);
|
|
position_and_write(journal, 0L, &header, sizeof(header));
|
|
|
|
return id;
|
|
}
|
|
|
|
|
|
static SLONG block_number(segment, block)
|
|
USHORT segment;
|
|
USHORT block;
|
|
{
|
|
/**************************************
|
|
*
|
|
* b l o c k _ n u m b e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Translate from (segment, block) to physical block number.
|
|
*
|
|
**************************************/
|
|
|
|
return segment * SEGMENT_SIZE + block + SEGMENT_OFFSET;
|
|
}
|
|
|
|
|
|
static clear(block, size)
|
|
UCHAR *block;
|
|
USHORT size;
|
|
{
|
|
/**************************************
|
|
*
|
|
* c l e a r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Clear a hunk of memory.
|
|
*
|
|
**************************************/
|
|
|
|
if (size)
|
|
do
|
|
*block++ = 0;
|
|
while (--size);
|
|
}
|
|
|
|
|
|
static delete_dir(header, id_space, name_string, name_length)
|
|
JFH header;
|
|
TEXT *id_space;
|
|
TEXT *name_string;
|
|
USHORT name_length;
|
|
{
|
|
/**************************************
|
|
*
|
|
* d e l e t e _ d i r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Delete a database entry from the journal file.
|
|
*
|
|
**************************************/
|
|
DIR dir, next;
|
|
USHORT l;
|
|
|
|
if (!(dir = locate_dir(header, id_space, name_string, name_length)))
|
|
return FAILURE;
|
|
|
|
next = JIO_next_dir(dir);
|
|
l = (TEXT *) next - id_space;
|
|
move(next, dir, header->jfh_id_space - l);
|
|
header->jfh_id_space -= (TEXT *) next - (TEXT *) dir;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
static error(journal, value, string, operation)
|
|
JRN journal;
|
|
int value;
|
|
TEXT *string;
|
|
STATUS operation;
|
|
{
|
|
/**************************************
|
|
*
|
|
* e r r o r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* We've had an unexpected error -- punt.
|
|
*
|
|
**************************************/
|
|
SSHORT errcode;
|
|
|
|
ERR_post(isc_io_error,
|
|
gds_arg_string, string,
|
|
gds_arg_string, journal->jrn_server,
|
|
isc_arg_gds, operation, SYS_ERROR, value, 0);
|
|
}
|
|
|
|
|
|
#ifdef VMS
|
|
static extend_file(journal)
|
|
JRN journal;
|
|
{
|
|
/**************************************
|
|
*
|
|
* e x t e n d _ f i l e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Extend the length of a file on VMS using QIOs. This is
|
|
* a royal pain in the tush.
|
|
*
|
|
**************************************/
|
|
USHORT iosb[4], l;
|
|
struct dsc$descriptor_s desc;
|
|
ATR$ atr;
|
|
FIB$ fib;
|
|
FAT$ fat;
|
|
int status, old_size, new_size, delta;
|
|
SCHAR *p;
|
|
|
|
ISC_make_desc(&fib, &desc, sizeof(fib));
|
|
atr.atr$w_size = ATR$S_RECATTR;
|
|
atr.atr$w_type = ATR$C_RECATTR;
|
|
atr.atr$l_addr = &fat;
|
|
atr.atr$l_zero = 0;
|
|
|
|
/* Get current size */
|
|
|
|
p = &fib;
|
|
l = sizeof(fib);
|
|
do
|
|
*p++ = 0;
|
|
while (--l);
|
|
|
|
move(journal->jrn_did, fib.fib$w_did, sizeof(fib.fib$w_did));
|
|
move(journal->jrn_fid, fib.fib$w_fid, sizeof(fib.fib$w_fid));
|
|
fib.fib$w_nmctl = FIB$M_FINDFID;
|
|
|
|
status = sys$qiow(15, /* Event flag */
|
|
journal->jrn_channel, /* Channel */
|
|
IO$_ACCESS, /* Function */
|
|
iosb, /* IO status block */
|
|
NULL, /* AST address */
|
|
NULL, /* AST parameter */
|
|
&desc, /* P1 (buffer) */
|
|
NULL, /* P2 (length) */
|
|
NULL, /* P3 (virtual block) */
|
|
NULL, /* P4 */
|
|
&atr, /* P5 */
|
|
NULL); /* P6 */
|
|
|
|
if (!(status & 1) || !((status = iosb[0]) & 1))
|
|
error(journal, status, "sys$enqw (journal io$_access)",
|
|
isc_io_access_err);
|
|
|
|
old_size = fat.fat$w_hiblk[1] + (fat.fat$w_hiblk[0] << 16);
|
|
new_size = old_size + MIN(old_size, 16000);
|
|
delta = new_size - old_size;
|
|
|
|
/* Extend file */
|
|
|
|
fib.fib$l_exsz = delta;
|
|
fib.fib$l_exvbn = 0;
|
|
fib.fib$l_acctl = FIB$M_WRITETHRU;
|
|
fib.fib$w_exctl = FIB$M_EXTEND | FIB$M_ALDEF | FIB$M_ALCONB;
|
|
|
|
fat.fat$w_efblk[0] = (new_size + 1) >> 16;
|
|
fat.fat$w_efblk[1] = (new_size + 1);
|
|
|
|
status = sys$qiow(15, /* Event flag */
|
|
journal->jrn_channel, /* Channel */
|
|
IO$_MODIFY, /* Function */
|
|
iosb, /* IO status block */
|
|
NULL, /* AST address */
|
|
NULL, /* AST parameter */
|
|
&desc, /* P1 (buffer) */
|
|
NULL, /* P2 (length) */
|
|
NULL, /* P3 (virtual block) */
|
|
NULL, /* P4 */
|
|
&atr, /* P5 */
|
|
NULL); /* P6 */
|
|
|
|
if (!(status & 1) || !((status = iosb[0]) & 1))
|
|
error(journal, status, "sys$enqw (journal io$_modify)",
|
|
isc_io_access_err);
|
|
}
|
|
#endif
|
|
|
|
|
|
static extend_segments(journal)
|
|
JRN journal;
|
|
{
|
|
/**************************************
|
|
*
|
|
* e x t e n d _ s e g m e n t s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Allocate new data segment for journal. If the file
|
|
* is full, return -1.
|
|
*
|
|
**************************************/
|
|
USHORT segment, slots;
|
|
struct jfh header;
|
|
|
|
position_and_read(journal, 0L, &header, sizeof(header));
|
|
|
|
/* Find a free segment. Allocate one if nothing is available */
|
|
|
|
if (header.jfh_free < SEGMENT_SLOTS)
|
|
segment = header.jfh_segments[header.jfh_free++];
|
|
else if (header.jfh_data + 1 >= header.jfh_free)
|
|
return -1;
|
|
else
|
|
segment = header.jfh_allocation++;
|
|
|
|
header.jfh_segments[++header.jfh_data] = segment;
|
|
header.jfh_write_block = 0;
|
|
position_and_write(journal, 0L, &header, sizeof(header));
|
|
|
|
return segment;
|
|
}
|
|
|
|
|
|
#ifdef VMS
|
|
static journal_open(journal, data)
|
|
JRN journal;
|
|
LDATA *data;
|
|
{
|
|
/**************************************
|
|
*
|
|
* j o u r n a l _ o p e n ( V M S )
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Open the journal file. If a new one is created
|
|
* return TRUE, otherwise return FALSE.
|
|
*
|
|
**************************************/
|
|
USHORT new, l, iosb[4];
|
|
int status;
|
|
TEXT *address, expanded_name[NAM$C_MAXRSS], *op, lock_string[64], *p, *q;
|
|
struct FAB fab;
|
|
struct NAM nam;
|
|
LKSB lksb;
|
|
ITM items[2];
|
|
struct dsc$descriptor_s string;
|
|
|
|
new = FALSE;
|
|
|
|
fab = cc$rms_fab;
|
|
nam = cc$rms_nam;
|
|
fab.fab$l_alq = (SEGMENT_OFFSET + SEGMENT_SIZE * 10);
|
|
fab.fab$w_deq = SEGMENT_SIZE * 10;
|
|
fab.fab$l_nam = &nam;
|
|
fab.fab$b_fac = FAB$M_BIO | FAB$M_GET | FAB$M_PUT;
|
|
fab.fab$l_fop = FAB$M_UFO;
|
|
fab.fab$l_fna = journal->jrn_server;
|
|
fab.fab$b_fns = strlen(fab.fab$l_fna);
|
|
fab.fab$b_rfm = FAB$C_UDF;
|
|
fab.fab$b_shr = FAB$M_SHRGET | FAB$M_SHRPUT | FAB$M_UPI;
|
|
|
|
nam.nam$l_rsa = expanded_name;
|
|
nam.nam$b_rss = sizeof(expanded_name);
|
|
|
|
op = "sys$open";
|
|
status = sys$open(&fab);
|
|
|
|
/* If the open failed, by creating the file instead */
|
|
|
|
if (status == RMS$_FNF) {
|
|
op = "sys$create";
|
|
status = sys$create(&fab);
|
|
new = TRUE;
|
|
}
|
|
|
|
/* If neither worked, punt */
|
|
|
|
if (!(status & 1))
|
|
error(journal, status, op, isc_io_open_err);
|
|
|
|
journal->jrn_channel = fab.fab$l_stv;
|
|
move(nam.nam$w_did, journal->jrn_did, sizeof(journal->jrn_did));
|
|
move(nam.nam$w_fid, journal->jrn_fid, sizeof(journal->jrn_fid));
|
|
|
|
/* Create lock string for journal file. This is truely a
|
|
pain in the ass since this must work anywhere on a
|
|
cluster */
|
|
|
|
items[0].itm_code = DVI$_DEVLOCKNAM;
|
|
items[0].itm_length = sizeof(lock_string);
|
|
items[0].itm_buffer = lock_string;
|
|
items[0].itm_return_length = &l;
|
|
|
|
items[1].itm_code = 0;
|
|
items[1].itm_length = 0;
|
|
items[1].itm_buffer = 0;
|
|
|
|
status = sys$getdviw(15, journal->jrn_channel, NULL,
|
|
items, iosb, NULL, NULL, NULL);
|
|
|
|
if (status != 1)
|
|
error(journal, status, "sys$getdviw", isc_io_access_err);
|
|
|
|
move(nam.nam$w_fid, lock_string + l, sizeof(nam.nam$w_fid));
|
|
ISC_make_desc(lock_string, &string, l + sizeof(nam.nam$w_fid));
|
|
|
|
status =
|
|
sys$enqw(EVENT_FLAG, LCK$K_PWMODE, &lksb, LCK$M_SYSTEM | LCK$M_VALBLK,
|
|
&string, 0, /* parent lock id */
|
|
NULL, /* AST routine when granted */
|
|
NULL, /* ast_argument */
|
|
NULL, /* ast_routine */
|
|
NULL, NULL);
|
|
|
|
if (!(status & 1))
|
|
error(journal, status, "sys$enqw", isc_io_access_err);
|
|
|
|
status = lksb.lksb_status;
|
|
|
|
if (status == SS$_VALNOTVALID)
|
|
data->data_long = 0;
|
|
else if (!(status & 1))
|
|
error(journal, status, "sys$enqw", isc_io_access_err);
|
|
|
|
journal->jrn_lock_id = lksb.lksb_lock_id;
|
|
|
|
if (data)
|
|
data->data_long = lksb.lksb_value[0];
|
|
|
|
return new;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifndef VMS
|
|
static journal_open(journal, data)
|
|
JRN journal;
|
|
LDATA *data;
|
|
{
|
|
/**************************************
|
|
*
|
|
* j o u r n a l _ o p e n ( U N I X )
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Open the journal file. If a new one is created
|
|
* return TRUE, otherwise return FALSE.
|
|
*
|
|
**************************************/
|
|
USHORT new;
|
|
int n, mask;
|
|
|
|
new = FALSE;
|
|
|
|
/* Try to open the journal file. If we can't, try to create it. If we
|
|
create it, format it. If we can't open or create, try to open one
|
|
last time. */
|
|
|
|
n = FILE_OPEN(journal->jrn_server, O_RDWR | O_BINARY, 0666);
|
|
|
|
if (n == -1) {
|
|
mask = umask(0);
|
|
n =
|
|
FILE_OPEN(journal->jrn_server,
|
|
O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0666);
|
|
umask(mask);
|
|
if (n == -1)
|
|
n = FILE_OPEN(journal->jrn_server, O_RDWR | O_BINARY, 0666);
|
|
else
|
|
new = TRUE;
|
|
}
|
|
|
|
if (n == -1)
|
|
error(journal, errno, "open", isc_io_open_err);
|
|
|
|
journal->jrn_channel = (int *) n;
|
|
lock_create(journal, data);
|
|
|
|
return new;
|
|
}
|
|
#endif
|
|
|
|
|
|
static DIR locate_dir(header, id_space, name_string, name_length)
|
|
JFH header;
|
|
TEXT *id_space;
|
|
TEXT *name_string;
|
|
USHORT name_length;
|
|
{
|
|
/**************************************
|
|
*
|
|
* l o c a t e _ d i r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Find database entry in database id record.
|
|
*
|
|
**************************************/
|
|
DIR dir, end;
|
|
|
|
end = (DIR) (id_space + header->jfh_id_space);
|
|
|
|
for (dir = (DIR) id_space; dir < end; dir = JIO_next_dir(dir))
|
|
if (name_length == dir->dir_length
|
|
&& !strncmp(dir->dir_name, name_string, name_length))
|
|
return dir;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
#ifdef VMS
|
|
static lock_create(journal, data)
|
|
JRN journal;
|
|
LDATA *data;
|
|
{
|
|
/**************************************
|
|
*
|
|
* l o c k _ c r e a t e ( V M S )
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Create lock for journal file.
|
|
*
|
|
**************************************/
|
|
struct dsc$descriptor string;
|
|
TEXT filename[256];
|
|
SLONG status;
|
|
LKSB lksb;
|
|
|
|
getname(journal->jrn_channel, filename, 1);
|
|
filename[31] = 0;
|
|
ISC_make_desc(filename, &string, 0);
|
|
|
|
status =
|
|
sys$enqw(EVENT_FLAG, LCK$K_PWMODE, &lksb, LCK$M_SYSTEM | LCK$M_VALBLK,
|
|
&string, 0, /* parent lock id */
|
|
NULL, /* AST routine when granted */
|
|
NULL, /* ast_argument */
|
|
NULL, /* ast_routine */
|
|
NULL, NULL);
|
|
|
|
if (!(status & 1))
|
|
error(journal, status, "sys$enqw", isc_io_access_err);
|
|
|
|
status = lksb.lksb_status;
|
|
|
|
if (status == SS$_VALNOTVALID)
|
|
data->data_long = 0;
|
|
else if (!(status & 1))
|
|
error(journal, status, "sys$enqw", isc_io_access_err);
|
|
|
|
journal->jrn_lock_id = lksb.lksb_lock_id;
|
|
|
|
if (data)
|
|
data->data_long = lksb.lksb_value[0];
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifndef VMS
|
|
static lock_create(journal, data)
|
|
JRN journal;
|
|
LDATA *data;
|
|
{
|
|
/**************************************
|
|
*
|
|
* l o c k _ c r e a t e ( U N I X )
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Create lock for journal file.
|
|
*
|
|
**************************************/
|
|
|
|
journal->jrn_lock_id = LOCK_enq(0L, /* Prior */
|
|
0L, /* Parent */
|
|
0, /* Series */
|
|
journal->jrn_server, /* Lock string */
|
|
strlen(journal->jrn_server), /* length of lock string */
|
|
LCK_PW,
|
|
0, 0, 0L, 1, journal->jrn_status_vector);
|
|
|
|
data->data_long = LOCK_read_data(journal->jrn_lock_id);
|
|
}
|
|
#endif
|
|
|
|
|
|
static lock_exclusive(journal, flag)
|
|
JRN journal;
|
|
SSHORT flag;
|
|
{
|
|
/**************************************
|
|
*
|
|
* l o c k _ e x c l u s i v e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Try to get an exclusive lock on the journal file, but
|
|
* don't block trying. Return TRUE if we succeed, FALSE
|
|
* otherwise.
|
|
*
|
|
**************************************/
|
|
SSHORT readers, writers;
|
|
|
|
#ifdef VMS
|
|
return vms_convert(journal, NULL, LCK$K_EXMODE, FALSE);
|
|
#endif
|
|
|
|
#ifndef VMS
|
|
return LOCK_convert(journal->jrn_lock_id, LCK_EX, 0, 0, 0,
|
|
journal->jrn_status_vector);
|
|
#endif
|
|
}
|
|
|
|
|
|
static lock_release(journal)
|
|
JRN journal;
|
|
{
|
|
/**************************************
|
|
*
|
|
* l o c k _ r e l e a s e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Release the lock on the journal file.
|
|
*
|
|
**************************************/
|
|
|
|
#ifdef VMS
|
|
sys$setast(1);
|
|
sys$deq(journal->jrn_lock_id, NULL, NULL, NULL);
|
|
#endif
|
|
|
|
#ifndef VMS
|
|
LOCK_deq(journal->jrn_lock_id);
|
|
#endif
|
|
}
|
|
|
|
|
|
static SLONG lookup_id(journal, name_string, name_length)
|
|
JRN journal;
|
|
TEXT *name_string;
|
|
{
|
|
/**************************************
|
|
*
|
|
* l o o k u p _ i d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup database id.
|
|
*
|
|
**************************************/
|
|
struct jfh header;
|
|
TEXT id_space[ID_SPACE];
|
|
DIR dir;
|
|
|
|
position_and_read(journal, 0L, &header, sizeof(header));
|
|
position_and_read(journal, (SLONG) ID_OFFSET, id_space,
|
|
header.jfh_id_space);
|
|
|
|
if (dir = locate_dir(&header, id_space, name_string, name_length))
|
|
return dir->dir_id;
|
|
|
|
return 0L;
|
|
}
|
|
|
|
|
|
static high_water(journal, header)
|
|
JRN journal;
|
|
JFH header;
|
|
{
|
|
/**************************************
|
|
*
|
|
* h i g h _ w a t e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* We have exclusive access to an existing journal file. It
|
|
* is possible that the last guy didn't update the "last write"
|
|
* slot before going away. Try to find the last valid block
|
|
* written, and from that, the next block available.
|
|
*
|
|
**************************************/
|
|
USHORT block, segment;
|
|
struct jrnh record;
|
|
|
|
segment = header->jfh_segments[header->jfh_data];
|
|
|
|
for (block = header->jfh_write_block; block < SEGMENT_SIZE;
|
|
block += BLOCKS(record.jrnh_length)) {
|
|
position_and_read(journal, block_number(segment, block), &record,
|
|
sizeof(record));
|
|
if (record.jrnh_type < JRN_ENABLE || record.jrnh_type > JRN_DUMMY
|
|
|| record.jrnh_series != header->jfh_series
|
|
|| record.jrnh_handle > header->jfh_next_id)
|
|
return block;
|
|
}
|
|
|
|
return SEGMENT_SIZE;
|
|
}
|
|
|
|
|
|
static move(from, to, length)
|
|
UCHAR *from;
|
|
UCHAR *to;
|
|
USHORT length;
|
|
{
|
|
/**************************************
|
|
*
|
|
* m o v e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Move some bytes.
|
|
*
|
|
**************************************/
|
|
|
|
if (length)
|
|
do
|
|
*to++ = *from++;
|
|
while (--length);
|
|
}
|
|
|
|
|
|
#ifdef VMS
|
|
static position_and_read(journal, block, buffer, length)
|
|
JRN journal;
|
|
ULONG block;
|
|
UCHAR *buffer;
|
|
USHORT length;
|
|
{
|
|
/**************************************
|
|
*
|
|
* p o s i t i o n _ a n d _ r e a d ( V M S )
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Position journal file to particular block and read record.
|
|
*
|
|
**************************************/
|
|
SSHORT iosb[4];
|
|
int status;
|
|
|
|
if (!length)
|
|
return;
|
|
|
|
status = sys$qiow(15, /* Event flag */
|
|
journal->jrn_channel, /* Channel */
|
|
IO$_READVBLK, /* Function */
|
|
iosb, /* IO status block */
|
|
NULL, /* AST address */
|
|
NULL, /* AST parameter */
|
|
buffer, /* P1 (buffer) */
|
|
length, /* P2 (length) */
|
|
block + 1, /* P3 (virtual block) */
|
|
NULL, NULL, NULL);
|
|
|
|
if (status == SS$_ENDOFFILE) {
|
|
do
|
|
*buffer++ = 0;
|
|
while (--length);
|
|
return;
|
|
}
|
|
|
|
if (!(status & 1))
|
|
error(journal, status, "sys$qiow (journal read)", isc_io_read_err);
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifndef VMS
|
|
static position_and_read(journal, block, buffer, length)
|
|
JRN journal;
|
|
ULONG block;
|
|
UCHAR *buffer;
|
|
USHORT length;
|
|
{
|
|
/**************************************
|
|
*
|
|
* p o s i t i o n _ a n d _ r e a d ( U N I X )
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Position journal file to particular block and read record.
|
|
*
|
|
**************************************/
|
|
int n;
|
|
SLONG position;
|
|
|
|
position = (SLONG) (block * BLOCK_SIZE);
|
|
n = lseek((int) journal->jrn_channel, LSEEK_OFFSET_CAST position, 0);
|
|
|
|
if (n == -1)
|
|
error(journal, errno, "lseek", isc_io_read_err);
|
|
|
|
n = read((int) journal->jrn_channel, buffer, length);
|
|
|
|
if (n == -1)
|
|
error(journal, errno, "read", isc_io_read_err);
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef VMS
|
|
static position_and_write(journal, block, buffer, length)
|
|
JRN journal;
|
|
ULONG block;
|
|
UCHAR *buffer;
|
|
USHORT length;
|
|
{
|
|
/**************************************
|
|
*
|
|
* p o s i t i o n _ a n d _ w r i t e ( V M S )
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Position journal file to particular block and write record.
|
|
*
|
|
**************************************/
|
|
SSHORT iosb[4];
|
|
int status;
|
|
|
|
for (;;) {
|
|
status = sys$qiow(15, /* Event flag */
|
|
journal->jrn_channel, /* Channel */
|
|
IO$_WRITEVBLK, /* Function */
|
|
iosb, /* IO status block */
|
|
NULL, /* AST address */
|
|
NULL, /* AST parameter */
|
|
buffer, /* P1 (buffer) */
|
|
length, /* P2 (length) */
|
|
block + 1, /* P3 (virtual block) */
|
|
NULL, NULL, NULL);
|
|
|
|
if ((status & 1) && ((status = iosb[0]) & 1))
|
|
break;
|
|
|
|
if (status == SS$_ENDOFFILE)
|
|
extend_file(journal);
|
|
else
|
|
error(journal, status, "sys$qiow (journal write)",
|
|
isc_io_write_err);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifndef VMS
|
|
static position_and_write(journal, block, buffer, length)
|
|
JRN journal;
|
|
ULONG block;
|
|
UCHAR *buffer;
|
|
USHORT length;
|
|
{
|
|
/**************************************
|
|
*
|
|
* p o s i t i o n _ a n d _ w r i t e ( u n i x )
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Position journal file to particular block and write record.
|
|
*
|
|
**************************************/
|
|
int position, n;
|
|
|
|
position = block * BLOCK_SIZE;
|
|
n = lseek((int) journal->jrn_channel, LSEEK_OFFSET_CAST position, 0);
|
|
|
|
if (n == -1)
|
|
error(journal, errno, "lseek", isc_io_write_err);
|
|
|
|
n = write((int) journal->jrn_channel, buffer, length);
|
|
|
|
if (n == -1)
|
|
error(journal, errno, "write", isc_io_write_err);
|
|
}
|
|
#endif
|
|
|
|
|
|
static release(journal, data)
|
|
JRN journal;
|
|
LDATA *data;
|
|
{
|
|
/**************************************
|
|
*
|
|
* r e l e a s e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Down grade file and and update lock block.
|
|
*
|
|
**************************************/
|
|
|
|
#ifdef VMS
|
|
vms_convert(journal, data, LCK$K_CRMODE, TRUE);
|
|
#endif
|
|
|
|
#ifndef VMS
|
|
LOCK_write_data(journal->jrn_lock_id, data->data_long);
|
|
LOCK_convert(journal->jrn_lock_id, LCK_SR, TRUE, 0, 0,
|
|
journal->jrn_status_vector);
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef VMS
|
|
static vms_convert(journal, data, mode, wait)
|
|
JRN journal;
|
|
LDATA *data;
|
|
{
|
|
/**************************************
|
|
*
|
|
* v m s _ c o n v e r t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Acquire write lock on journal file, getting lock data block.
|
|
*
|
|
**************************************/
|
|
SLONG status, flags;
|
|
LKSB lksb;
|
|
|
|
lksb.lksb_lock_id = journal->jrn_lock_id;
|
|
|
|
if (data) {
|
|
if (mode == LCK$K_PWMODE || mode == LCK$K_EXMODE)
|
|
sys$setast(0);
|
|
lksb.lksb_value[0] = data->data_long;
|
|
}
|
|
|
|
flags = LCK$M_CONVERT;
|
|
|
|
if (data)
|
|
flags |= LCK$M_VALBLK;
|
|
|
|
if (!wait)
|
|
flags |= LCK$M_NOQUEUE;
|
|
|
|
status = sys$enqw(EVENT_FLAG, mode, &lksb, flags, NULL, NULL, NULL, /* AST routine when granted */
|
|
NULL, /* ast_argument */
|
|
NULL, /* ast_routine */
|
|
NULL, NULL);
|
|
|
|
if (!wait && status == SS$_NOTQUEUED)
|
|
return FALSE;
|
|
|
|
if (!(status & 1))
|
|
error(journal, status, "sys$enqw", isc_io_access_err);
|
|
|
|
status = lksb.lksb_status;
|
|
|
|
if (!(status & 1))
|
|
error(journal, status, "sys$enqw", isc_io_access_err);
|
|
|
|
if (data) {
|
|
data->data_long = lksb.lksb_value[0];
|
|
if (!(mode == LCK$K_PWMODE || mode == LCK$K_EXMODE))
|
|
sys$setast(1);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
#endif |