1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* md.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* This code manages relations that reside on magnetic disk.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
1999-10-25 05:08:03 +02:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.57 1999/10/25 03:07:47 tgl Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1996-11-08 07:02:30 +01:00
|
|
|
#include <unistd.h>
|
1999-07-16 05:14:30 +02:00
|
|
|
#include <fcntl.h>
|
1996-07-09 08:22:35 +02:00
|
|
|
#include <sys/file.h>
|
|
|
|
|
|
|
|
#include "postgres.h"
|
|
|
|
|
1998-01-13 05:05:12 +01:00
|
|
|
#include "catalog/catalog.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "miscadmin.h"
|
1999-07-16 05:14:30 +02:00
|
|
|
#include "storage/smgr.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
#undef DIAGNOSTIC
|
|
|
|
|
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* The magnetic disk storage manager keeps track of open file descriptors
|
|
|
|
* in its own descriptor pool. This happens for two reasons. First, at
|
|
|
|
* transaction boundaries, we walk the list of descriptors and flush
|
1999-04-06 00:25:11 +02:00
|
|
|
* anything that we've dirtied in the current transaction. Second, we want
|
|
|
|
* to support relations larger than the OS' file size limit (often 2GBytes).
|
|
|
|
* In order to do that, we break relations up into chunks of < 2GBytes
|
|
|
|
* and store one chunk in each of several files that represent the relation.
|
|
|
|
* See the BLCKSZ and RELSEG_SIZE configuration constants in include/config.h.
|
1999-09-02 04:57:50 +02:00
|
|
|
*
|
|
|
|
* The file descriptor stored in the relation cache (see RelationGetFile())
|
|
|
|
* is actually an index into the Md_fdvec array. -1 indicates not open.
|
|
|
|
*
|
|
|
|
* When a relation is broken into multiple chunks, only the first chunk
|
|
|
|
* has its own entry in the Md_fdvec array; the remaining chunks have
|
|
|
|
* palloc'd MdfdVec objects that are chained onto the first chunk via the
|
|
|
|
* mdfd_chain links. All chunks except the last MUST have size exactly
|
|
|
|
* equal to RELSEG_SIZE blocks --- see mdnblocks() and mdtruncate().
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
typedef struct _MdfdVec
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int mdfd_vfd; /* fd number in vfd pool */
|
|
|
|
uint16 mdfd_flags; /* clean, dirty, free */
|
|
|
|
int mdfd_lstbcnt; /* most recent block count */
|
|
|
|
int mdfd_nextFree; /* next free vector */
|
1998-03-20 05:22:54 +01:00
|
|
|
#ifndef LET_OS_MANAGE_FILESIZE
|
1997-09-07 07:04:48 +02:00
|
|
|
struct _MdfdVec *mdfd_chain;/* for large relations */
|
1998-03-20 05:22:54 +01:00
|
|
|
#endif
|
1997-09-08 23:56:23 +02:00
|
|
|
} MdfdVec;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-09-02 04:57:50 +02:00
|
|
|
static int Nfds = 100; /* initial/current size of Md_fdvec array */
|
1997-09-07 07:04:48 +02:00
|
|
|
static MdfdVec *Md_fdvec = (MdfdVec *) NULL;
|
1999-09-02 04:57:50 +02:00
|
|
|
static int Md_Free = -1; /* head of freelist of unused fdvec entries */
|
|
|
|
static int CurFd = 0; /* first never-used fdvec index */
|
|
|
|
static MemoryContext MdCxt; /* context for all my allocations */
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
#define MDFD_DIRTY (uint16) 0x01
|
|
|
|
#define MDFD_FREE (uint16) 0x02
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
/* routines declared here */
|
1999-09-02 04:57:50 +02:00
|
|
|
static int _mdfd_getrelnfd(Relation reln);
|
1997-09-07 07:04:48 +02:00
|
|
|
static MdfdVec *_mdfd_openseg(Relation reln, int segno, int oflags);
|
1999-09-02 04:57:50 +02:00
|
|
|
static MdfdVec *_mdfd_getseg(Relation reln, int blkno);
|
1997-09-08 04:41:22 +02:00
|
|
|
static int _fdvec_alloc(void);
|
|
|
|
static void _fdvec_free(int);
|
1996-07-09 08:22:35 +02:00
|
|
|
static BlockNumber _mdnblocks(File file, Size blcksz);
|
|
|
|
|
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* mdinit() -- Initialize private state for magnetic disk storage manager.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* We keep a private table of all file descriptors. Whenever we do
|
|
|
|
* a write to one, we mark it dirty in our table. Whenever we force
|
|
|
|
* changes to disk, we mark the file descriptor clean. At transaction
|
|
|
|
* commit, we force changes to disk for all dirty file descriptors.
|
|
|
|
* This routine allocates and initializes the table.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* Returns SM_SUCCESS or SM_FAIL with errno set as appropriate.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
mdinit()
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
MemoryContext oldcxt;
|
|
|
|
int i;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
MdCxt = (MemoryContext) CreateGlobalMemory("MdSmgr");
|
|
|
|
if (MdCxt == (MemoryContext) NULL)
|
1998-09-01 05:29:17 +02:00
|
|
|
return SM_FAIL;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
oldcxt = MemoryContextSwitchTo(MdCxt);
|
|
|
|
Md_fdvec = (MdfdVec *) palloc(Nfds * sizeof(MdfdVec));
|
|
|
|
MemoryContextSwitchTo(oldcxt);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (Md_fdvec == (MdfdVec *) NULL)
|
1998-09-01 05:29:17 +02:00
|
|
|
return SM_FAIL;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-18 22:22:58 +02:00
|
|
|
MemSet(Md_fdvec, 0, Nfds * sizeof(MdfdVec));
|
1997-05-22 19:08:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* Set free list */
|
|
|
|
for (i = 0; i < Nfds; i++)
|
|
|
|
{
|
|
|
|
Md_fdvec[i].mdfd_nextFree = i + 1;
|
|
|
|
Md_fdvec[i].mdfd_flags = MDFD_FREE;
|
|
|
|
}
|
|
|
|
Md_Free = 0;
|
|
|
|
Md_fdvec[Nfds - 1].mdfd_nextFree = -1;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return SM_SUCCESS;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
mdcreate(Relation reln)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int fd,
|
|
|
|
vfd;
|
|
|
|
char *path;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-07-20 18:14:18 +02:00
|
|
|
path = relpath(reln->rd_rel->relname.data);
|
1999-01-17 07:20:06 +01:00
|
|
|
#ifndef __CYGWIN32__
|
1997-09-07 07:04:48 +02:00
|
|
|
fd = FileNameOpenFile(path, O_RDWR | O_CREAT | O_EXCL, 0600);
|
1999-01-17 07:20:06 +01:00
|
|
|
#else
|
|
|
|
fd = FileNameOpenFile(path, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600);
|
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the file already exists and is empty, we pretend that the create
|
|
|
|
* succeeded. During bootstrap processing, we skip that check,
|
|
|
|
* because pg_time, pg_variable, and pg_log get created before their
|
|
|
|
* .bki file entries are processed.
|
|
|
|
*
|
|
|
|
* As the result of this pretence it was possible to have in pg_class > 1
|
|
|
|
* records with the same relname. Actually, it should be fixed in
|
|
|
|
* upper levels, too, but... - vadim 05/06/97
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (fd < 0)
|
|
|
|
{
|
|
|
|
if (!IsBootstrapProcessingMode())
|
1998-09-01 05:29:17 +02:00
|
|
|
return -1;
|
1999-01-17 07:20:06 +01:00
|
|
|
#ifndef __CYGWIN32__
|
1997-09-07 07:04:48 +02:00
|
|
|
fd = FileNameOpenFile(path, O_RDWR, 0600); /* Bootstrap */
|
1999-01-17 07:20:06 +01:00
|
|
|
#else
|
|
|
|
fd = FileNameOpenFile(path, O_RDWR | O_BINARY, 0600); /* Bootstrap */
|
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
if (fd < 0)
|
1998-09-01 05:29:17 +02:00
|
|
|
return -1;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
vfd = _fdvec_alloc();
|
|
|
|
if (vfd < 0)
|
1998-09-01 05:29:17 +02:00
|
|
|
return -1;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
Md_fdvec[vfd].mdfd_vfd = fd;
|
|
|
|
Md_fdvec[vfd].mdfd_flags = (uint16) 0;
|
1998-03-20 05:22:54 +01:00
|
|
|
#ifndef LET_OS_MANAGE_FILESIZE
|
1997-09-07 07:04:48 +02:00
|
|
|
Md_fdvec[vfd].mdfd_chain = (MdfdVec *) NULL;
|
1998-03-20 05:22:54 +01:00
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
Md_fdvec[vfd].mdfd_lstbcnt = 0;
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return vfd;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* mdunlink() -- Unlink a relation.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
mdunlink(Relation reln)
|
|
|
|
{
|
1999-09-02 04:57:50 +02:00
|
|
|
int nblocks;
|
1997-09-08 04:41:22 +02:00
|
|
|
int fd;
|
1999-09-02 04:57:50 +02:00
|
|
|
MdfdVec *v;
|
1997-09-08 04:41:22 +02:00
|
|
|
MemoryContext oldcxt;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
1999-09-02 04:57:50 +02:00
|
|
|
* Force all segments of the relation to be opened, so that we
|
|
|
|
* won't miss deleting any of them.
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
1999-09-02 04:57:50 +02:00
|
|
|
nblocks = mdnblocks(reln);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-09-02 04:57:50 +02:00
|
|
|
/*
|
|
|
|
* Clean out the mdfd vector, letting fd.c unlink the physical files.
|
|
|
|
*
|
|
|
|
* NOTE: We truncate the file(s) before deleting 'em, because if other
|
|
|
|
* backends are holding the files open, the unlink will fail on some
|
|
|
|
* platforms (think Microsoft). Better a zero-size file gets left around
|
|
|
|
* than a big file. Those other backends will be forced to close the
|
|
|
|
* relation by cache invalidation, but that probably hasn't happened yet.
|
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
fd = RelationGetFile(reln);
|
1999-09-02 04:57:50 +02:00
|
|
|
if (fd < 0) /* should not happen */
|
|
|
|
elog(ERROR, "mdunlink: mdnblocks didn't open relation");
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
Md_fdvec[fd].mdfd_flags = (uint16) 0;
|
|
|
|
|
|
|
|
oldcxt = MemoryContextSwitchTo(MdCxt);
|
1998-03-20 05:22:54 +01:00
|
|
|
#ifndef LET_OS_MANAGE_FILESIZE
|
1997-09-07 07:04:48 +02:00
|
|
|
for (v = &Md_fdvec[fd]; v != (MdfdVec *) NULL;)
|
|
|
|
{
|
1999-09-02 04:57:50 +02:00
|
|
|
MdfdVec *ov = v;
|
|
|
|
FileTruncate(v->mdfd_vfd, 0);
|
1997-09-07 07:04:48 +02:00
|
|
|
FileUnlink(v->mdfd_vfd);
|
|
|
|
v = v->mdfd_chain;
|
|
|
|
if (ov != &Md_fdvec[fd])
|
|
|
|
pfree(ov);
|
|
|
|
}
|
|
|
|
Md_fdvec[fd].mdfd_chain = (MdfdVec *) NULL;
|
1998-03-20 05:22:54 +01:00
|
|
|
#else
|
|
|
|
v = &Md_fdvec[fd];
|
1999-09-02 04:57:50 +02:00
|
|
|
FileTruncate(v->mdfd_vfd, 0);
|
|
|
|
FileUnlink(v->mdfd_vfd);
|
1998-03-20 05:22:54 +01:00
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
MemoryContextSwitchTo(oldcxt);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
_fdvec_free(fd);
|
|
|
|
|
1999-09-02 04:57:50 +02:00
|
|
|
/* be sure to mark relation closed */
|
|
|
|
reln->rd_fd = -1;
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return SM_SUCCESS;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* mdextend() -- Add a block to the specified relation.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* This routine returns SM_FAIL or SM_SUCCESS, with errno set as
|
|
|
|
* appropriate.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
mdextend(Relation reln, char *buffer)
|
|
|
|
{
|
1999-10-06 08:38:04 +02:00
|
|
|
long pos, nbytes;
|
1997-09-08 04:41:22 +02:00
|
|
|
int nblocks;
|
|
|
|
MdfdVec *v;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
nblocks = mdnblocks(reln);
|
1999-09-02 04:57:50 +02:00
|
|
|
v = _mdfd_getseg(reln, nblocks);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if ((pos = FileSeek(v->mdfd_vfd, 0L, SEEK_END)) < 0)
|
1998-09-01 05:29:17 +02:00
|
|
|
return SM_FAIL;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-10-06 08:38:04 +02:00
|
|
|
if (pos % BLCKSZ != 0) /* the last block is incomplete */
|
|
|
|
{
|
|
|
|
pos -= pos % BLCKSZ;
|
|
|
|
if (FileSeek(v->mdfd_vfd, pos, SEEK_SET) < 0)
|
|
|
|
return SM_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((nbytes = FileWrite(v->mdfd_vfd, buffer, BLCKSZ)) != BLCKSZ)
|
|
|
|
{
|
|
|
|
if (nbytes > 0)
|
|
|
|
{
|
|
|
|
FileTruncate(v->mdfd_vfd, pos);
|
|
|
|
FileSeek(v->mdfd_vfd, pos, SEEK_SET);
|
|
|
|
}
|
1998-09-01 05:29:17 +02:00
|
|
|
return SM_FAIL;
|
1999-10-06 08:38:04 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* remember that we did a write, so we can sync at xact commit */
|
|
|
|
v->mdfd_flags |= MDFD_DIRTY;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* try to keep the last block count current, though it's just a hint */
|
1998-03-20 05:22:54 +01:00
|
|
|
#ifndef LET_OS_MANAGE_FILESIZE
|
1997-09-07 07:04:48 +02:00
|
|
|
if ((v->mdfd_lstbcnt = (++nblocks % RELSEG_SIZE)) == 0)
|
|
|
|
v->mdfd_lstbcnt = RELSEG_SIZE;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
1997-09-07 07:04:48 +02:00
|
|
|
if (_mdnblocks(v->mdfd_vfd, BLCKSZ) > RELSEG_SIZE
|
|
|
|
|| v->mdfd_lstbcnt > RELSEG_SIZE)
|
|
|
|
elog(FATAL, "segment too big!");
|
1996-07-09 08:22:35 +02:00
|
|
|
#endif
|
1998-03-20 05:22:54 +01:00
|
|
|
#else
|
|
|
|
v->mdfd_lstbcnt = ++nblocks;
|
|
|
|
#endif
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return SM_SUCCESS;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* mdopen() -- Open the specified relation.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
mdopen(Relation reln)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
char *path;
|
|
|
|
int fd;
|
|
|
|
int vfd;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-07-20 18:14:18 +02:00
|
|
|
path = relpath(reln->rd_rel->relname.data);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-01-17 07:20:06 +01:00
|
|
|
#ifndef __CYGWIN32__
|
1997-09-07 07:04:48 +02:00
|
|
|
fd = FileNameOpenFile(path, O_RDWR, 0600);
|
1999-01-17 07:20:06 +01:00
|
|
|
#else
|
|
|
|
fd = FileNameOpenFile(path, O_RDWR | O_BINARY, 0600);
|
|
|
|
#endif
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (fd < 0)
|
1999-09-06 01:24:53 +02:00
|
|
|
{
|
|
|
|
/* in bootstrap mode, accept mdopen as substitute for mdcreate */
|
|
|
|
if (IsBootstrapProcessingMode())
|
|
|
|
{
|
1999-01-17 07:20:06 +01:00
|
|
|
#ifndef __CYGWIN32__
|
1999-09-06 01:24:53 +02:00
|
|
|
fd = FileNameOpenFile(path, O_RDWR | O_CREAT | O_EXCL, 0600);
|
1999-01-17 07:20:06 +01:00
|
|
|
#else
|
1999-09-06 01:24:53 +02:00
|
|
|
fd = FileNameOpenFile(path, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600);
|
1999-01-17 07:20:06 +01:00
|
|
|
#endif
|
1999-09-06 01:24:53 +02:00
|
|
|
}
|
|
|
|
if (fd < 0)
|
|
|
|
{
|
|
|
|
elog(ERROR, "mdopen: couldn't open %s: %m", path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
vfd = _fdvec_alloc();
|
|
|
|
if (vfd < 0)
|
1998-09-01 05:29:17 +02:00
|
|
|
return -1;
|
1997-05-22 19:08:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
Md_fdvec[vfd].mdfd_vfd = fd;
|
|
|
|
Md_fdvec[vfd].mdfd_flags = (uint16) 0;
|
|
|
|
Md_fdvec[vfd].mdfd_lstbcnt = _mdnblocks(fd, BLCKSZ);
|
1998-03-20 05:22:54 +01:00
|
|
|
#ifndef LET_OS_MANAGE_FILESIZE
|
|
|
|
Md_fdvec[vfd].mdfd_chain = (MdfdVec *) NULL;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
1997-09-07 07:04:48 +02:00
|
|
|
if (Md_fdvec[vfd].mdfd_lstbcnt > RELSEG_SIZE)
|
|
|
|
elog(FATAL, "segment too big on relopen!");
|
1998-03-20 05:22:54 +01:00
|
|
|
#endif
|
1996-07-09 08:22:35 +02:00
|
|
|
#endif
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return vfd;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1999-09-02 04:57:50 +02:00
|
|
|
* mdclose() -- Close the specified relation, if it isn't closed already.
|
1997-05-22 19:08:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* AND FREE fd vector! It may be re-used for other relation!
|
|
|
|
* reln should be flushed from cache after closing !..
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* Returns SM_SUCCESS or SM_FAIL with errno set as appropriate.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
mdclose(Relation reln)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int fd;
|
1999-09-02 04:57:50 +02:00
|
|
|
MdfdVec *v;
|
1997-09-08 04:41:22 +02:00
|
|
|
MemoryContext oldcxt;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
fd = RelationGetFile(reln);
|
1999-09-02 04:57:50 +02:00
|
|
|
if (fd < 0)
|
|
|
|
return SM_SUCCESS; /* already closed, so no work */
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
oldcxt = MemoryContextSwitchTo(MdCxt);
|
1998-03-20 05:22:54 +01:00
|
|
|
#ifndef LET_OS_MANAGE_FILESIZE
|
1997-09-07 07:04:48 +02:00
|
|
|
for (v = &Md_fdvec[fd]; v != (MdfdVec *) NULL;)
|
1997-05-22 19:08:35 +02:00
|
|
|
{
|
1999-09-02 04:57:50 +02:00
|
|
|
MdfdVec *ov = v;
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* if not closed already */
|
|
|
|
if (v->mdfd_vfd >= 0)
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We sync the file descriptor so that we don't need to reopen
|
|
|
|
* it at transaction commit to force changes to disk.
|
|
|
|
*/
|
|
|
|
|
|
|
|
FileSync(v->mdfd_vfd);
|
|
|
|
FileClose(v->mdfd_vfd);
|
|
|
|
|
|
|
|
/* mark this file descriptor as clean in our private table */
|
|
|
|
v->mdfd_flags &= ~MDFD_DIRTY;
|
|
|
|
}
|
|
|
|
/* Now free vector */
|
|
|
|
v = v->mdfd_chain;
|
|
|
|
if (ov != &Md_fdvec[fd])
|
|
|
|
pfree(ov);
|
|
|
|
}
|
1997-05-22 19:08:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
Md_fdvec[fd].mdfd_chain = (MdfdVec *) NULL;
|
1998-03-20 05:22:54 +01:00
|
|
|
#else
|
|
|
|
v = &Md_fdvec[fd];
|
|
|
|
if (v != (MdfdVec *) NULL)
|
|
|
|
{
|
|
|
|
if (v->mdfd_vfd >= 0)
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We sync the file descriptor so that we don't need to reopen
|
|
|
|
* it at transaction commit to force changes to disk.
|
|
|
|
*/
|
|
|
|
|
|
|
|
FileSync(v->mdfd_vfd);
|
|
|
|
FileClose(v->mdfd_vfd);
|
|
|
|
|
|
|
|
/* mark this file descriptor as clean in our private table */
|
|
|
|
v->mdfd_flags &= ~MDFD_DIRTY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
MemoryContextSwitchTo(oldcxt);
|
1997-05-22 19:08:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
_fdvec_free(fd);
|
|
|
|
|
1999-09-02 04:57:50 +02:00
|
|
|
/* be sure to mark relation closed */
|
|
|
|
reln->rd_fd = -1;
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return SM_SUCCESS;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* mdread() -- Read the specified block from a relation.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* Returns SM_SUCCESS or SM_FAIL.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
mdread(Relation reln, BlockNumber blocknum, char *buffer)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int status;
|
|
|
|
long seekpos;
|
|
|
|
int nbytes;
|
|
|
|
MdfdVec *v;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-09-02 04:57:50 +02:00
|
|
|
v = _mdfd_getseg(reln, blocknum);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-03-20 05:22:54 +01:00
|
|
|
#ifndef LET_OS_MANAGE_FILESIZE
|
1997-09-07 07:04:48 +02:00
|
|
|
seekpos = (long) (BLCKSZ * (blocknum % RELSEG_SIZE));
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
1997-09-07 07:04:48 +02:00
|
|
|
if (seekpos >= BLCKSZ * RELSEG_SIZE)
|
|
|
|
elog(FATAL, "seekpos too big!");
|
1996-07-09 08:22:35 +02:00
|
|
|
#endif
|
1998-03-20 05:22:54 +01:00
|
|
|
#else
|
|
|
|
seekpos = (long) (BLCKSZ * (blocknum));
|
|
|
|
#endif
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (FileSeek(v->mdfd_vfd, seekpos, SEEK_SET) != seekpos)
|
1998-09-01 05:29:17 +02:00
|
|
|
return SM_FAIL;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
status = SM_SUCCESS;
|
|
|
|
if ((nbytes = FileRead(v->mdfd_vfd, buffer, BLCKSZ)) != BLCKSZ)
|
|
|
|
{
|
|
|
|
if (nbytes == 0)
|
1997-09-18 22:22:58 +02:00
|
|
|
MemSet(buffer, 0, BLCKSZ);
|
1999-10-06 08:38:04 +02:00
|
|
|
else if (blocknum == 0 && nbytes > 0 && mdnblocks(reln) == 0)
|
|
|
|
MemSet(buffer, 0, BLCKSZ);
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
|
|
|
status = SM_FAIL;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return status;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* mdwrite() -- Write the supplied block at the appropriate location.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* Returns SM_SUCCESS or SM_FAIL.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
mdwrite(Relation reln, BlockNumber blocknum, char *buffer)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int status;
|
|
|
|
long seekpos;
|
|
|
|
MdfdVec *v;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-09-02 04:57:50 +02:00
|
|
|
v = _mdfd_getseg(reln, blocknum);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-03-20 05:22:54 +01:00
|
|
|
#ifndef LET_OS_MANAGE_FILESIZE
|
1997-09-07 07:04:48 +02:00
|
|
|
seekpos = (long) (BLCKSZ * (blocknum % RELSEG_SIZE));
|
1996-07-09 08:22:35 +02:00
|
|
|
#ifdef DIAGNOSTIC
|
1997-09-07 07:04:48 +02:00
|
|
|
if (seekpos >= BLCKSZ * RELSEG_SIZE)
|
|
|
|
elog(FATAL, "seekpos too big!");
|
1996-07-09 08:22:35 +02:00
|
|
|
#endif
|
1998-03-20 05:22:54 +01:00
|
|
|
#else
|
|
|
|
seekpos = (long) (BLCKSZ * (blocknum));
|
|
|
|
#endif
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (FileSeek(v->mdfd_vfd, seekpos, SEEK_SET) != seekpos)
|
1998-09-01 05:29:17 +02:00
|
|
|
return SM_FAIL;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
status = SM_SUCCESS;
|
|
|
|
if (FileWrite(v->mdfd_vfd, buffer, BLCKSZ) != BLCKSZ)
|
|
|
|
status = SM_FAIL;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
v->mdfd_flags |= MDFD_DIRTY;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return status;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* mdflush() -- Synchronously write a block to disk.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* This is exactly like mdwrite(), but doesn't return until the file
|
|
|
|
* system buffer cache has been flushed.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
mdflush(Relation reln, BlockNumber blocknum, char *buffer)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int status;
|
|
|
|
long seekpos;
|
|
|
|
MdfdVec *v;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-09-02 04:57:50 +02:00
|
|
|
v = _mdfd_getseg(reln, blocknum);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-03-20 05:22:54 +01:00
|
|
|
#ifndef LET_OS_MANAGE_FILESIZE
|
1997-09-07 07:04:48 +02:00
|
|
|
seekpos = (long) (BLCKSZ * (blocknum % RELSEG_SIZE));
|
1996-07-09 08:22:35 +02:00
|
|
|
#ifdef DIAGNOSTIC
|
1997-09-07 07:04:48 +02:00
|
|
|
if (seekpos >= BLCKSZ * RELSEG_SIZE)
|
|
|
|
elog(FATAL, "seekpos too big!");
|
1996-07-09 08:22:35 +02:00
|
|
|
#endif
|
1998-03-20 05:22:54 +01:00
|
|
|
#else
|
|
|
|
seekpos = (long) (BLCKSZ * (blocknum));
|
|
|
|
#endif
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (FileSeek(v->mdfd_vfd, seekpos, SEEK_SET) != seekpos)
|
1998-09-01 05:29:17 +02:00
|
|
|
return SM_FAIL;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* write and sync the block */
|
|
|
|
status = SM_SUCCESS;
|
|
|
|
if (FileWrite(v->mdfd_vfd, buffer, BLCKSZ) != BLCKSZ
|
|
|
|
|| FileSync(v->mdfd_vfd) < 0)
|
|
|
|
status = SM_FAIL;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
|
|
|
* By here, the block is written and changes have been forced to
|
|
|
|
* stable storage. Mark the descriptor as clean until the next write,
|
|
|
|
* so we don't sync it again unnecessarily at transaction commit.
|
|
|
|
*/
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
v->mdfd_flags &= ~MDFD_DIRTY;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return status;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* mdblindwrt() -- Write a block to disk blind.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* We have to be able to do this using only the name and OID of
|
|
|
|
* the database and relation in which the block belongs. This
|
|
|
|
* is a synchronous write.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
mdblindwrt(char *dbstr,
|
1997-09-07 07:04:48 +02:00
|
|
|
char *relstr,
|
|
|
|
Oid dbid,
|
|
|
|
Oid relid,
|
|
|
|
BlockNumber blkno,
|
|
|
|
char *buffer)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int fd;
|
|
|
|
int segno;
|
|
|
|
long seekpos;
|
|
|
|
int status;
|
|
|
|
char *path;
|
1998-03-20 05:22:54 +01:00
|
|
|
|
|
|
|
#ifndef LET_OS_MANAGE_FILESIZE
|
1997-09-08 04:41:22 +02:00
|
|
|
int nchars;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* be sure we have enough space for the '.segno', if any */
|
|
|
|
segno = blkno / RELSEG_SIZE;
|
|
|
|
if (segno > 0)
|
|
|
|
nchars = 10;
|
1996-07-09 08:22:35 +02:00
|
|
|
else
|
1997-09-07 07:04:48 +02:00
|
|
|
nchars = 0;
|
|
|
|
|
|
|
|
/* construct the path to the file and open it */
|
1997-11-07 07:38:51 +01:00
|
|
|
/* system table? then put in system area... */
|
1997-09-07 07:04:48 +02:00
|
|
|
if (dbid == (Oid) 0)
|
|
|
|
{
|
|
|
|
path = (char *) palloc(strlen(DataDir) + sizeof(NameData) + 2 + nchars);
|
|
|
|
if (segno == 0)
|
|
|
|
sprintf(path, "%s/%s", DataDir, relstr);
|
|
|
|
else
|
|
|
|
sprintf(path, "%s/%s.%d", DataDir, relstr, segno);
|
|
|
|
}
|
1997-11-07 07:38:51 +01:00
|
|
|
/* user table? then put in user database area... */
|
1998-02-23 14:58:04 +01:00
|
|
|
else if (dbid == MyDatabaseId)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1998-02-26 05:46:47 +01:00
|
|
|
extern char *DatabasePath;
|
|
|
|
|
1998-02-23 14:58:04 +01:00
|
|
|
path = (char *) palloc(strlen(DatabasePath) + 2 * sizeof(NameData) + 2 + nchars);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (segno == 0)
|
1998-02-23 14:58:04 +01:00
|
|
|
sprintf(path, "%s%c%s", DatabasePath, SEP_CHAR, relstr);
|
1997-09-07 07:04:48 +02:00
|
|
|
else
|
1998-02-23 14:58:04 +01:00
|
|
|
sprintf(path, "%s%c%s.%d", DatabasePath, SEP_CHAR, relstr, segno);
|
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
else
|
|
|
|
/* this is work arround only !!! */
|
1998-02-23 14:58:04 +01:00
|
|
|
{
|
1999-10-25 05:08:03 +02:00
|
|
|
char dbpath[MAXPGPATH];
|
1998-08-11 20:28:49 +02:00
|
|
|
Oid id;
|
1998-02-26 05:46:47 +01:00
|
|
|
char *tmpPath;
|
|
|
|
|
1999-09-24 02:25:33 +02:00
|
|
|
GetRawDatabaseInfo(dbstr, &id, dbpath);
|
1998-02-26 05:46:47 +01:00
|
|
|
|
1998-02-23 14:58:04 +01:00
|
|
|
if (id != dbid)
|
1998-02-26 05:46:47 +01:00
|
|
|
elog(FATAL, "mdblindwrt: oid of db %s is not %u", dbstr, dbid);
|
1998-02-23 14:58:04 +01:00
|
|
|
tmpPath = ExpandDatabasePath(dbpath);
|
|
|
|
if (tmpPath == NULL)
|
1998-02-26 05:46:47 +01:00
|
|
|
elog(FATAL, "mdblindwrt: can't expand path for db %s", dbstr);
|
1998-02-23 14:58:04 +01:00
|
|
|
path = (char *) palloc(strlen(tmpPath) + 2 * sizeof(NameData) + 2 + nchars);
|
1997-11-07 07:38:51 +01:00
|
|
|
if (segno == 0)
|
1998-02-23 14:58:04 +01:00
|
|
|
sprintf(path, "%s%c%s", tmpPath, SEP_CHAR, relstr);
|
1997-11-07 07:38:51 +01:00
|
|
|
else
|
1998-02-23 14:58:04 +01:00
|
|
|
sprintf(path, "%s%c%s.%d", tmpPath, SEP_CHAR, relstr, segno);
|
1998-02-26 05:46:47 +01:00
|
|
|
pfree(tmpPath);
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1998-03-20 05:22:54 +01:00
|
|
|
#else
|
|
|
|
/* construct the path to the file and open it */
|
|
|
|
/* system table? then put in system area... */
|
|
|
|
if (dbid == (Oid) 0)
|
|
|
|
{
|
|
|
|
path = (char *) palloc(strlen(DataDir) + sizeof(NameData) + 2);
|
|
|
|
sprintf(path, "%s/%s", DataDir, relstr);
|
|
|
|
}
|
|
|
|
/* user table? then put in user database area... */
|
|
|
|
else if (dbid == MyDatabaseId)
|
|
|
|
{
|
|
|
|
extern char *DatabasePath;
|
|
|
|
|
|
|
|
path = (char *) palloc(strlen(DatabasePath) + 2 * sizeof(NameData) + 2);
|
|
|
|
sprintf(path, "%s%c%s", DatabasePath, SEP_CHAR, relstr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
/* this is work arround only !!! */
|
|
|
|
{
|
1999-10-25 05:08:03 +02:00
|
|
|
char dbpath[MAXPGPATH];
|
1998-08-11 20:28:49 +02:00
|
|
|
Oid id;
|
1998-03-20 05:22:54 +01:00
|
|
|
char *tmpPath;
|
1998-07-24 05:32:46 +02:00
|
|
|
|
1999-09-24 02:25:33 +02:00
|
|
|
GetRawDatabaseInfo(dbstr, &id, dbpath);
|
1998-03-20 05:22:54 +01:00
|
|
|
|
|
|
|
if (id != dbid)
|
|
|
|
elog(FATAL, "mdblindwrt: oid of db %s is not %u", dbstr, dbid);
|
|
|
|
tmpPath = ExpandDatabasePath(dbpath);
|
|
|
|
if (tmpPath == NULL)
|
|
|
|
elog(FATAL, "mdblindwrt: can't expand path for db %s", dbstr);
|
|
|
|
path = (char *) palloc(strlen(tmpPath) + 2 * sizeof(NameData) + 2);
|
|
|
|
sprintf(path, "%s%c%s", tmpPath, SEP_CHAR, relstr);
|
|
|
|
pfree(tmpPath);
|
|
|
|
}
|
|
|
|
#endif
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-01-17 07:20:06 +01:00
|
|
|
#ifndef __CYGWIN32__
|
1997-09-07 07:04:48 +02:00
|
|
|
if ((fd = open(path, O_RDWR, 0600)) < 0)
|
1999-01-17 07:20:06 +01:00
|
|
|
#else
|
|
|
|
if ((fd = open(path, O_RDWR | O_BINARY, 0600)) < 0)
|
|
|
|
#endif
|
1998-09-01 05:29:17 +02:00
|
|
|
return SM_FAIL;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* seek to the right spot */
|
1998-03-20 05:22:54 +01:00
|
|
|
#ifndef LET_OS_MANAGE_FILESIZE
|
1997-09-07 07:04:48 +02:00
|
|
|
seekpos = (long) (BLCKSZ * (blkno % RELSEG_SIZE));
|
1998-03-20 05:22:54 +01:00
|
|
|
#else
|
|
|
|
seekpos = (long) (BLCKSZ * (blkno));
|
|
|
|
#endif
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (lseek(fd, seekpos, SEEK_SET) != seekpos)
|
|
|
|
{
|
|
|
|
close(fd);
|
1998-09-01 05:29:17 +02:00
|
|
|
return SM_FAIL;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
status = SM_SUCCESS;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* write and sync the block */
|
|
|
|
if (write(fd, buffer, BLCKSZ) != BLCKSZ || (pg_fsync(fd) < 0))
|
|
|
|
status = SM_FAIL;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (close(fd) < 0)
|
|
|
|
status = SM_FAIL;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
pfree(path);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return status;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* mdnblocks() -- Get the number of blocks stored in a relation.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1999-09-02 04:57:50 +02:00
|
|
|
* Important side effect: all segments of the relation are opened
|
|
|
|
* and added to the mdfd_chain list. If this routine has not been
|
|
|
|
* called, then only segments up to the last one actually touched
|
|
|
|
* are present in the chain...
|
|
|
|
*
|
|
|
|
* Returns # of blocks, elog's on error.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
mdnblocks(Relation reln)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int fd;
|
|
|
|
MdfdVec *v;
|
1999-09-02 04:57:50 +02:00
|
|
|
#ifndef LET_OS_MANAGE_FILESIZE
|
1997-09-08 04:41:22 +02:00
|
|
|
int nblocks;
|
|
|
|
int segno;
|
1999-09-02 04:57:50 +02:00
|
|
|
#endif
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1999-09-02 04:57:50 +02:00
|
|
|
fd = _mdfd_getrelnfd(reln);
|
1997-09-07 07:04:48 +02:00
|
|
|
v = &Md_fdvec[fd];
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-03-20 05:22:54 +01:00
|
|
|
#ifndef LET_OS_MANAGE_FILESIZE
|
1997-09-07 07:04:48 +02:00
|
|
|
segno = 0;
|
|
|
|
for (;;)
|
|
|
|
{
|
1999-06-18 18:47:23 +02:00
|
|
|
nblocks = _mdnblocks(v->mdfd_vfd, BLCKSZ);
|
|
|
|
if (nblocks > RELSEG_SIZE)
|
|
|
|
elog(FATAL, "segment too big in mdnblocks!");
|
|
|
|
v->mdfd_lstbcnt = nblocks;
|
|
|
|
if (nblocks == RELSEG_SIZE)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
segno++;
|
|
|
|
|
|
|
|
if (v->mdfd_chain == (MdfdVec *) NULL)
|
|
|
|
{
|
|
|
|
v->mdfd_chain = _mdfd_openseg(reln, segno, O_CREAT);
|
|
|
|
if (v->mdfd_chain == (MdfdVec *) NULL)
|
1998-07-20 18:57:18 +02:00
|
|
|
elog(ERROR, "cannot count blocks for %s -- open failed",
|
1997-09-07 07:04:48 +02:00
|
|
|
RelationGetRelationName(reln));
|
|
|
|
}
|
|
|
|
|
|
|
|
v = v->mdfd_chain;
|
|
|
|
}
|
|
|
|
else
|
1998-09-01 05:29:17 +02:00
|
|
|
return (segno * RELSEG_SIZE) + nblocks;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1998-03-20 05:22:54 +01:00
|
|
|
#else
|
1998-09-01 05:29:17 +02:00
|
|
|
return _mdnblocks(v->mdfd_vfd, BLCKSZ);
|
1998-03-20 05:22:54 +01:00
|
|
|
#endif
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1996-11-27 08:24:02 +01:00
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* mdtruncate() -- Truncate relation to specified number of blocks.
|
1996-11-27 08:24:02 +01:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* Returns # of blocks or -1 on error.
|
1996-11-27 08:24:02 +01:00
|
|
|
*/
|
|
|
|
int
|
1997-09-07 07:04:48 +02:00
|
|
|
mdtruncate(Relation reln, int nblocks)
|
1996-11-27 08:24:02 +01:00
|
|
|
{
|
1999-09-02 04:57:50 +02:00
|
|
|
int curnblk;
|
1997-09-08 04:41:22 +02:00
|
|
|
int fd;
|
|
|
|
MdfdVec *v;
|
1998-03-20 05:22:54 +01:00
|
|
|
#ifndef LET_OS_MANAGE_FILESIZE
|
1999-09-02 04:57:50 +02:00
|
|
|
MemoryContext oldcxt;
|
|
|
|
int priorblocks;
|
|
|
|
#endif
|
1996-11-27 08:24:02 +01:00
|
|
|
|
1999-09-02 04:57:50 +02:00
|
|
|
/* NOTE: mdnblocks makes sure we have opened all existing segments,
|
|
|
|
* so that truncate/delete loop will get them all!
|
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
curnblk = mdnblocks(reln);
|
1999-09-02 04:57:50 +02:00
|
|
|
if (nblocks < 0 || nblocks > curnblk)
|
|
|
|
return -1; /* bogus request */
|
|
|
|
if (nblocks == curnblk)
|
|
|
|
return nblocks; /* no work */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-09-02 04:57:50 +02:00
|
|
|
fd = _mdfd_getrelnfd(reln);
|
1997-09-07 07:04:48 +02:00
|
|
|
v = &Md_fdvec[fd];
|
1996-11-27 08:24:02 +01:00
|
|
|
|
1999-06-18 18:47:23 +02:00
|
|
|
#ifndef LET_OS_MANAGE_FILESIZE
|
1999-09-02 04:57:50 +02:00
|
|
|
oldcxt = MemoryContextSwitchTo(MdCxt);
|
|
|
|
priorblocks = 0;
|
|
|
|
while (v != (MdfdVec *) NULL)
|
1999-06-18 18:47:23 +02:00
|
|
|
{
|
1999-09-02 04:57:50 +02:00
|
|
|
MdfdVec *ov = v;
|
|
|
|
|
|
|
|
if (priorblocks > nblocks)
|
1999-06-18 18:47:23 +02:00
|
|
|
{
|
1999-09-02 04:57:50 +02:00
|
|
|
/* This segment is no longer wanted at all (and has already been
|
|
|
|
* unlinked from the mdfd_chain).
|
|
|
|
* We truncate the file before deleting it because if other
|
|
|
|
* backends are holding the file open, the unlink will fail on
|
|
|
|
* some platforms. Better a zero-size file gets left around than
|
|
|
|
* a big file...
|
|
|
|
*/
|
|
|
|
FileTruncate(v->mdfd_vfd, 0);
|
|
|
|
FileUnlink(v->mdfd_vfd);
|
|
|
|
v = v->mdfd_chain;
|
|
|
|
Assert(ov != &Md_fdvec[fd]); /* we never drop the 1st segment */
|
|
|
|
pfree(ov);
|
1999-06-18 18:47:23 +02:00
|
|
|
}
|
1999-09-02 04:57:50 +02:00
|
|
|
else if (priorblocks + RELSEG_SIZE > nblocks)
|
|
|
|
{
|
|
|
|
/* This is the last segment we want to keep.
|
|
|
|
* Truncate the file to the right length, and clear chain link
|
|
|
|
* that points to any remaining segments (which we shall zap).
|
|
|
|
* NOTE: if nblocks is exactly a multiple K of RELSEG_SIZE,
|
|
|
|
* we will truncate the K+1st segment to 0 length but keep it.
|
|
|
|
* This is mainly so that the right thing happens if nblocks=0.
|
|
|
|
*/
|
|
|
|
int lastsegblocks = nblocks - priorblocks;
|
|
|
|
if (FileTruncate(v->mdfd_vfd, lastsegblocks * BLCKSZ) < 0)
|
|
|
|
return -1;
|
|
|
|
v->mdfd_lstbcnt = lastsegblocks;
|
|
|
|
v = v->mdfd_chain;
|
|
|
|
ov->mdfd_chain = (MdfdVec *) NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* We still need this segment and 0 or more blocks beyond it,
|
|
|
|
* so nothing to do here.
|
|
|
|
*/
|
|
|
|
v = v->mdfd_chain;
|
|
|
|
}
|
|
|
|
priorblocks += RELSEG_SIZE;
|
1999-06-18 18:47:23 +02:00
|
|
|
}
|
1999-09-02 04:57:50 +02:00
|
|
|
MemoryContextSwitchTo(oldcxt);
|
1999-06-18 18:47:23 +02:00
|
|
|
#else
|
1997-09-07 07:04:48 +02:00
|
|
|
if (FileTruncate(v->mdfd_vfd, nblocks * BLCKSZ) < 0)
|
1998-09-01 05:29:17 +02:00
|
|
|
return -1;
|
1999-06-18 18:47:23 +02:00
|
|
|
v->mdfd_lstbcnt = nblocks;
|
|
|
|
#endif
|
1996-11-27 08:24:02 +01:00
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return nblocks;
|
1996-11-27 08:24:02 +01:00
|
|
|
|
1998-02-26 05:46:47 +01:00
|
|
|
} /* mdtruncate */
|
1996-11-27 08:24:02 +01:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* mdcommit() -- Commit a transaction.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* All changes to magnetic disk relations must be forced to stable
|
|
|
|
* storage. This routine makes a pass over the private table of
|
|
|
|
* file descriptors. Any descriptors to which we have done writes,
|
|
|
|
* but not synced, are synced here.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* Returns SM_SUCCESS or SM_FAIL with errno set as appropriate.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
mdcommit()
|
|
|
|
{
|
1999-09-28 13:41:09 +02:00
|
|
|
#ifdef XLOG
|
|
|
|
sync();
|
|
|
|
sleep(1);
|
|
|
|
sync();
|
|
|
|
return SM_SUCCESS;
|
|
|
|
#else
|
1997-09-08 04:41:22 +02:00
|
|
|
int i;
|
|
|
|
MdfdVec *v;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
for (i = 0; i < CurFd; i++)
|
|
|
|
{
|
1998-03-20 05:22:54 +01:00
|
|
|
#ifndef LET_OS_MANAGE_FILESIZE
|
1997-09-07 07:04:48 +02:00
|
|
|
for (v = &Md_fdvec[i]; v != (MdfdVec *) NULL; v = v->mdfd_chain)
|
1998-03-20 05:22:54 +01:00
|
|
|
#else
|
|
|
|
v = &Md_fdvec[i];
|
|
|
|
if (v != (MdfdVec *) NULL)
|
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
if (v->mdfd_flags & MDFD_DIRTY)
|
|
|
|
{
|
|
|
|
if (FileSync(v->mdfd_vfd) < 0)
|
1998-09-01 05:29:17 +02:00
|
|
|
return SM_FAIL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
v->mdfd_flags &= ~MDFD_DIRTY;
|
|
|
|
}
|
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return SM_SUCCESS;
|
1999-09-28 13:41:09 +02:00
|
|
|
#endif /* XLOG */
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* mdabort() -- Abort a transaction.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
1997-09-07 07:04:48 +02:00
|
|
|
* Changes need not be forced to disk at transaction abort. We mark
|
|
|
|
* all file descriptors as clean here. Always returns SM_SUCCESS.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
mdabort()
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int i;
|
|
|
|
MdfdVec *v;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
for (i = 0; i < CurFd; i++)
|
|
|
|
{
|
1998-03-20 05:22:54 +01:00
|
|
|
#ifndef LET_OS_MANAGE_FILESIZE
|
1997-09-07 07:04:48 +02:00
|
|
|
for (v = &Md_fdvec[i]; v != (MdfdVec *) NULL; v = v->mdfd_chain)
|
1999-09-02 04:57:50 +02:00
|
|
|
v->mdfd_flags &= ~MDFD_DIRTY;
|
1998-03-20 05:22:54 +01:00
|
|
|
#else
|
|
|
|
v = &Md_fdvec[i];
|
1999-09-02 04:57:50 +02:00
|
|
|
v->mdfd_flags &= ~MDFD_DIRTY;
|
1998-03-20 05:22:54 +01:00
|
|
|
#endif
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return SM_SUCCESS;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* _fdvec_alloc () -- grab a free (or new) md file descriptor vector.
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
static
|
1997-09-07 07:04:48 +02:00
|
|
|
int
|
|
|
|
_fdvec_alloc()
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
MdfdVec *nvec;
|
|
|
|
int fdvec,
|
|
|
|
i;
|
|
|
|
MemoryContext oldcxt;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (Md_Free >= 0) /* get from free list */
|
1997-05-22 19:08:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
fdvec = Md_Free;
|
|
|
|
Md_Free = Md_fdvec[fdvec].mdfd_nextFree;
|
|
|
|
Assert(Md_fdvec[fdvec].mdfd_flags == MDFD_FREE);
|
|
|
|
Md_fdvec[fdvec].mdfd_flags = 0;
|
|
|
|
if (fdvec >= CurFd)
|
|
|
|
{
|
|
|
|
Assert(fdvec == CurFd);
|
|
|
|
CurFd++;
|
|
|
|
}
|
1998-09-01 05:29:17 +02:00
|
|
|
return fdvec;
|
1997-05-22 19:08:35 +02:00
|
|
|
}
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* Must allocate more room */
|
|
|
|
|
|
|
|
if (Nfds != CurFd)
|
|
|
|
elog(FATAL, "_fdvec_alloc error");
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
Nfds *= 2;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
oldcxt = MemoryContextSwitchTo(MdCxt);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
nvec = (MdfdVec *) palloc(Nfds * sizeof(MdfdVec));
|
1997-09-18 22:22:58 +02:00
|
|
|
MemSet(nvec, 0, Nfds * sizeof(MdfdVec));
|
1997-09-07 07:04:48 +02:00
|
|
|
memmove(nvec, (char *) Md_fdvec, CurFd * sizeof(MdfdVec));
|
|
|
|
pfree(Md_fdvec);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
MemoryContextSwitchTo(oldcxt);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
Md_fdvec = nvec;
|
1997-05-22 19:08:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* Set new free list */
|
|
|
|
for (i = CurFd; i < Nfds; i++)
|
|
|
|
{
|
|
|
|
Md_fdvec[i].mdfd_nextFree = i + 1;
|
|
|
|
Md_fdvec[i].mdfd_flags = MDFD_FREE;
|
|
|
|
}
|
|
|
|
Md_fdvec[Nfds - 1].mdfd_nextFree = -1;
|
|
|
|
Md_Free = CurFd + 1;
|
1997-05-22 19:08:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
fdvec = CurFd;
|
|
|
|
CurFd++;
|
|
|
|
Md_fdvec[fdvec].mdfd_flags = 0;
|
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return fdvec;
|
1997-05-22 19:08:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* _fdvec_free () -- free md file descriptor vector.
|
1997-05-22 19:08:35 +02:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
static
|
1997-09-07 07:04:48 +02:00
|
|
|
void
|
|
|
|
_fdvec_free(int fdvec)
|
1997-05-22 19:08:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
Assert(Md_Free < 0 || Md_fdvec[Md_Free].mdfd_flags == MDFD_FREE);
|
1999-09-02 04:57:50 +02:00
|
|
|
Assert(Md_fdvec[fdvec].mdfd_flags != MDFD_FREE);
|
1997-09-07 07:04:48 +02:00
|
|
|
Md_fdvec[fdvec].mdfd_nextFree = Md_Free;
|
|
|
|
Md_fdvec[fdvec].mdfd_flags = MDFD_FREE;
|
|
|
|
Md_Free = fdvec;
|
1997-05-22 19:08:35 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static MdfdVec *
|
|
|
|
_mdfd_openseg(Relation reln, int segno, int oflags)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
MemoryContext oldcxt;
|
|
|
|
MdfdVec *v;
|
|
|
|
int fd;
|
|
|
|
bool dofree;
|
|
|
|
char *path,
|
|
|
|
*fullpath;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* be sure we have enough space for the '.segno', if any */
|
|
|
|
path = relpath(RelationGetRelationName(reln)->data);
|
|
|
|
|
|
|
|
dofree = false;
|
|
|
|
if (segno > 0)
|
|
|
|
{
|
|
|
|
dofree = true;
|
|
|
|
fullpath = (char *) palloc(strlen(path) + 12);
|
|
|
|
sprintf(fullpath, "%s.%d", path, segno);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fullpath = path;
|
|
|
|
|
|
|
|
/* open the file */
|
1999-01-17 07:20:06 +01:00
|
|
|
#ifndef __CYGWIN32__
|
1999-09-02 04:57:50 +02:00
|
|
|
fd = FileNameOpenFile(fullpath, O_RDWR | oflags, 0600);
|
1999-01-17 07:20:06 +01:00
|
|
|
#else
|
1999-09-02 04:57:50 +02:00
|
|
|
fd = FileNameOpenFile(fullpath, O_RDWR | O_BINARY | oflags, 0600);
|
1999-01-17 07:20:06 +01:00
|
|
|
#endif
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (dofree)
|
|
|
|
pfree(fullpath);
|
|
|
|
|
|
|
|
if (fd < 0)
|
1998-09-01 05:29:17 +02:00
|
|
|
return (MdfdVec *) NULL;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/* allocate an mdfdvec entry for it */
|
|
|
|
oldcxt = MemoryContextSwitchTo(MdCxt);
|
|
|
|
v = (MdfdVec *) palloc(sizeof(MdfdVec));
|
|
|
|
MemoryContextSwitchTo(oldcxt);
|
|
|
|
|
|
|
|
/* fill the entry */
|
|
|
|
v->mdfd_vfd = fd;
|
|
|
|
v->mdfd_flags = (uint16) 0;
|
|
|
|
v->mdfd_lstbcnt = _mdnblocks(fd, BLCKSZ);
|
1998-03-20 05:22:54 +01:00
|
|
|
#ifndef LET_OS_MANAGE_FILESIZE
|
|
|
|
v->mdfd_chain = (MdfdVec *) NULL;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
1997-09-07 07:04:48 +02:00
|
|
|
if (v->mdfd_lstbcnt > RELSEG_SIZE)
|
|
|
|
elog(FATAL, "segment too big on open!");
|
1998-03-20 05:22:54 +01:00
|
|
|
#endif
|
1996-07-09 08:22:35 +02:00
|
|
|
#endif
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/* all done */
|
1998-09-01 05:29:17 +02:00
|
|
|
return v;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1999-09-02 04:57:50 +02:00
|
|
|
/* Get the fd for the relation, opening it if it's not already open */
|
|
|
|
|
|
|
|
static int
|
|
|
|
_mdfd_getrelnfd(Relation reln)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int fd;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
fd = RelationGetFile(reln);
|
|
|
|
if (fd < 0)
|
|
|
|
{
|
|
|
|
if ((fd = mdopen(reln)) < 0)
|
1998-07-20 18:57:18 +02:00
|
|
|
elog(ERROR, "cannot open relation %s",
|
1997-09-07 07:04:48 +02:00
|
|
|
RelationGetRelationName(reln));
|
|
|
|
reln->rd_fd = fd;
|
|
|
|
}
|
1999-09-02 04:57:50 +02:00
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find the segment of the relation holding the specified block */
|
|
|
|
|
|
|
|
static MdfdVec *
|
|
|
|
_mdfd_getseg(Relation reln, int blkno)
|
|
|
|
{
|
|
|
|
MdfdVec *v;
|
|
|
|
int segno;
|
|
|
|
int fd;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
fd = _mdfd_getrelnfd(reln);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-03-20 05:22:54 +01:00
|
|
|
#ifndef LET_OS_MANAGE_FILESIZE
|
1997-09-07 07:04:48 +02:00
|
|
|
for (v = &Md_fdvec[fd], segno = blkno / RELSEG_SIZE, i = 1;
|
|
|
|
segno > 0;
|
|
|
|
i++, segno--)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (v->mdfd_chain == (MdfdVec *) NULL)
|
|
|
|
{
|
1999-09-02 04:57:50 +02:00
|
|
|
v->mdfd_chain = _mdfd_openseg(reln, i, O_CREAT);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (v->mdfd_chain == (MdfdVec *) NULL)
|
1998-07-20 18:57:18 +02:00
|
|
|
elog(ERROR, "cannot open segment %d of relation %s",
|
1997-09-07 07:04:48 +02:00
|
|
|
i, RelationGetRelationName(reln));
|
|
|
|
}
|
|
|
|
v = v->mdfd_chain;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1998-03-20 05:22:54 +01:00
|
|
|
#else
|
|
|
|
v = &Md_fdvec[fd];
|
|
|
|
#endif
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-09-01 05:29:17 +02:00
|
|
|
return v;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
static BlockNumber
|
1996-07-09 08:22:35 +02:00
|
|
|
_mdnblocks(File file, Size blcksz)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
long len;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-10-06 08:38:04 +02:00
|
|
|
len = FileSeek(file, 0L, SEEK_END);
|
|
|
|
if (len < 0) return 0; /* on failure, assume file is empty */
|
|
|
|
return (BlockNumber) (len / blcksz);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|