blob: 594e325e76d5c6c5247f21eb58255a50462cf9af [file] [log] [blame]
/*
* Copyright (c) 2000-2001 Silicon Graphics, Inc.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <xfs/xfs.h>
#include <xfs/jdm.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include "config.h"
#include "types.h"
#include "lock.h"
#include "mlog.h"
#include "namreg.h"
#include "openutil.h"
#include "mmap.h"
#include "global.h"
#include "content.h"
#include "content_inode.h"
#include "dirattr.h"
/* structure definitions used locally ****************************************/
#define NAMREG_AVGLEN 10
/* persistent context for a namreg - placed in first page
* of the namreg file by namreg_init if not a sync
*/
struct namreg_pers {
off64_t np_appendoff;
};
typedef struct namreg_pers namreg_pers_t;
#define NAMREG_PERS_SZ pgsz
/* transient context for a namreg - allocated by namreg_init()
*/
#define NAMREG_BUFSIZE 32768
struct namreg_tran {
char *nt_pathname;
int nt_fd;
char *nt_map;
bool_t nt_at_endpr;
size_t nt_off;
char nt_buf[NAMREG_BUFSIZE];
};
typedef struct namreg_tran namreg_tran_t;
#ifdef NAMREGCHK
/* macros for manipulating namreg handles when handle consistency
* checking is enabled.
*/
#define CHKBITCNT 2
#define CHKBITSHIFT (NBBY * sizeof(nrh_t) - CHKBITCNT)
#define CHKBITLOMASK ((1ULL << CHKBITCNT) - 1)
#define CHKBITMASK (CHKBITLOMASK << CHKBITSHIFT)
#define CHKHDLCNT CHKBITSHIFT
#define CHKHDLMASK ((1ULL << CHKHDLCNT) - 1)
#define CHKGETBIT(h) (((h) >> CHKBITSHIFT) & CHKBITLOMASK)
#define CHKGETHDL(h) ((h) & CHKHDLMASK)
#define CHKMKHDL(c, h) ((((c) << CHKBITSHIFT) & CHKBITMASK) \
| \
((h) & CHKHDLMASK))
#define HDLMAX ((off64_t)CHKHDLMASK)
#else /* NAMREGCHK */
#define HDLMAX (NRH_NULL - 1)
#endif /* NAMREGCHK */
/* declarations of externally defined global symbols *************************/
extern size_t pgsz;
/* forward declarations of locally defined static functions ******************/
static rv_t namreg_flush(void);
/* definition of locally defined global variables ****************************/
/* definition of locally defined static variables *****************************/
static char *namregfile = "namreg";
static namreg_tran_t *ntp = 0;
static namreg_pers_t *npp = 0;
/* definition of locally defined global functions ****************************/
bool_t
namreg_init(char *hkdir, bool_t resume, uint64_t inocnt)
{
if (ntp) {
return BOOL_TRUE;
}
/* sanity checks
*/
assert(!ntp);
assert(!npp);
assert(sizeof(namreg_pers_t) <= NAMREG_PERS_SZ);
/* allocate and initialize context
*/
ntp = (namreg_tran_t *)calloc(1, sizeof(namreg_tran_t));
assert(ntp);
/* generate a string containing the pathname of the namreg file
*/
ntp->nt_pathname = open_pathalloc(hkdir, namregfile, 0);
/* open the namreg file
*/
if (resume) {
/* open existing file
*/
ntp->nt_fd = open(ntp->nt_pathname, O_RDWR);
if (ntp->nt_fd < 0) {
mlog(MLOG_NORMAL | MLOG_ERROR, _(
"could not find name registry file %s: "
"%s\n"),
ntp->nt_pathname,
strerror(errno));
return BOOL_FALSE;
}
} else {
ntp->nt_fd = create_filled_file(ntp->nt_pathname,
NAMREG_PERS_SZ + (inocnt * NAMREG_AVGLEN));
if (ntp->nt_fd < 0) {
mlog(MLOG_NORMAL | MLOG_ERROR, _(
"could not create name registry file %s: "
"%s\n"),
ntp->nt_pathname,
strerror(errno));
return BOOL_FALSE;
}
}
/* mmap the persistent descriptor
*/
assert(!(NAMREG_PERS_SZ % pgsz));
npp = (namreg_pers_t *) mmap_autogrow(
NAMREG_PERS_SZ,
ntp->nt_fd,
(off_t)0);
if (npp == (namreg_pers_t *)-1) {
mlog(MLOG_NORMAL | MLOG_ERROR, _(
"unable to map %s: %s\n"),
ntp->nt_pathname,
strerror(errno));
return BOOL_FALSE;
}
/* initialize persistent state
*/
if (!resume) {
npp->np_appendoff = (off64_t)NAMREG_PERS_SZ;
}
/* initialize transient state
*/
ntp->nt_at_endpr = BOOL_FALSE;
return BOOL_TRUE;
}
nrh_t
namreg_add(char *name, size_t namelen)
{
off64_t oldoff;
unsigned char c;
nrh_t nrh;
/* sanity checks
*/
assert(ntp);
assert(npp);
assert(!ntp->nt_map);
/* make sure file pointer is positioned to append
*/
if (!ntp->nt_at_endpr) {
off64_t newoff;
newoff = lseek64(ntp->nt_fd, npp->np_appendoff, SEEK_SET);
if (newoff == (off64_t)-1) {
mlog(MLOG_NORMAL, _(
"lseek of namreg failed: %s\n"),
strerror(errno));
assert(0);
return NRH_NULL;
}
assert(npp->np_appendoff == newoff);
ntp->nt_at_endpr = BOOL_TRUE;
}
if (ntp->nt_off + namelen + 1 > sizeof(ntp->nt_buf)) {
if (namreg_flush() != RV_OK) {
return NRH_NULL;
}
}
/* save the current offset
*/
oldoff = npp->np_appendoff;
/* write a one byte unsigned string length into the buffer.
*/
assert(namelen < 256);
c = (unsigned char)(namelen & 0xff);
ntp->nt_buf[ntp->nt_off++] = c;
/* write the name string into the buffer.
*/
memcpy(ntp->nt_buf + ntp->nt_off, name, namelen);
ntp->nt_off += namelen;
npp->np_appendoff += (off64_t)(1 + namelen);
assert(oldoff <= HDLMAX);
#ifdef NAMREGCHK
/* encode the lsb of the len plus the first character into the handle.
*/
nrh = CHKMKHDL((nrh_t)namelen + (nrh_t)*name, (nrh_t)oldoff);
#else /* NAMREGCHK */
nrh = (nrh_t)oldoff;
#endif /* NAMREGCHK */
return nrh;
}
/* ARGSUSED */
void
namreg_del(nrh_t nrh)
{
/* currently not implemented - grows, never shrinks
*/
}
static rv_t
namreg_flush(void)
{
ssize_t nwritten;
/* sanity checks
*/
assert(ntp);
if (ntp->nt_off) {
/* write the accumulated name strings.
*/
nwritten = write(ntp->nt_fd, (void *)ntp->nt_buf, ntp->nt_off);
if (nwritten != ntp->nt_off) {
if (nwritten < 0) {
mlog(MLOG_NORMAL | MLOG_ERROR,
_("write of namreg buffer failed: %s\n"),
strerror(errno));
} else {
mlog(MLOG_NORMAL | MLOG_ERROR,
_("write of namreg buffer failed: "
"expected to write %ld, actually "
"wrote %ld\n"), ntp->nt_off, nwritten);
}
assert(0);
return RV_UNKNOWN;
}
ntp->nt_off = 0;
}
return RV_OK;
}
int
namreg_get(nrh_t nrh,
char *bufp,
size_t bufsz)
{
off64_t newoff;
int nread;
size_t len;
char *in_bufp;
static char read_buf[256];
/* long enough for the longest allowed name (255), plus 1 for length */
#ifdef NAMREGCHK
nrh_t chkbit;
#endif /* NAMREGCHK */
/* sanity checks
*/
assert(ntp);
assert(npp);
/* make sure we aren't being given a NULL handle
*/
assert(nrh != NRH_NULL);
/* convert the handle into the offset
*/
#ifdef NAMREGCHK
newoff = (off64_t)(size64_t)CHKGETHDL(nrh);
chkbit = CHKGETBIT(nrh);
#else /* NAMREGCHK */
newoff = (off64_t)(size64_t)nrh;
#endif /* NAMREGCHK */
/* do sanity check on offset
*/
assert(newoff <= HDLMAX);
assert(newoff < npp->np_appendoff);
assert(newoff >= (off64_t)NAMREG_PERS_SZ);
lock();
if (ntp->nt_map) {
in_bufp = ntp->nt_map + newoff - NAMREG_PERS_SZ;
} else {
if (ntp->nt_at_endpr && ntp->nt_off) {
if (namreg_flush() != RV_OK) {
unlock();
return -3;
}
}
/* seek to the name
*/
newoff = lseek64(ntp->nt_fd, newoff, SEEK_SET);
if (newoff == (off64_t)-1) {
unlock();
mlog(MLOG_NORMAL, _(
"lseek of namreg failed: %s\n"),
strerror(errno));
return -3;
}
ntp->nt_at_endpr = BOOL_FALSE;
/* read the name length and the name itself in one call
* NOTE: assumes read_buf is big enough for the longest
* allowed name (255 chars) plus one byte for length.
*/
nread = read(ntp->nt_fd, (void *)read_buf, sizeof(read_buf));
if (nread <= 0) {
unlock();
mlog(MLOG_NORMAL, _(
"read of namreg failed: %s (nread = %d)\n"),
strerror(errno),
nread);
return -3;
}
in_bufp = read_buf;
}
/* deal with a short caller-supplied buffer
*/
len = (size_t)in_bufp[0];
if (bufsz < len + 1) {
unlock();
return -1;
}
/* copy the name into the caller-supplied buffer.
*/
strncpy(bufp, in_bufp+1, len);
#ifdef NAMREGCHK
/* validate the checkbit
*/
assert(chkbit
==
(((nrh_t)len + (nrh_t)bufp[0]) & CHKBITLOMASK));
#endif /* NAMREGCHK */
/* null-terminate the string if room
*/
bufp[len] = 0;
unlock();
return (int)len;
}
rv_t
namreg_map(void)
{
rv_t rv;
/* ensure all entries have been written */
if ((rv = namreg_flush()) != RV_OK) {
return rv;
}
ntp->nt_map = (char *) mmap_autogrow(
npp->np_appendoff - NAMREG_PERS_SZ,
ntp->nt_fd,
NAMREG_PERS_SZ);
/* it's okay if this fails, just fall back to (the much slower)
* seek-and-read lookups.
*/
if (ntp->nt_map == (char *)-1) {
mlog(MLOG_DEBUG, "failed to map namreg: %s\n",
strerror(errno));
ntp->nt_map = NULL;
}
return RV_OK;
}
/* definition of locally defined static functions ****************************/