blob: 562492e6b437eb58f9b3cc0f7fe9ba62c8c893fb [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 <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>
#include <time.h>
#include <fcntl.h>
#include <dirent.h>
#include <time.h>
#include "types.h"
#include "util.h"
#include "openutil.h"
#include "mlog.h"
#include "global.h"
#include "drive.h"
#include "media.h"
#include "content.h"
#include "content_inode.h"
#include "inomap.h"
#include "mmap.h"
#include "arch_xlate.h"
/* structure definitions used locally ****************************************/
/* restores the inomap into a file
*/
#define PERS_NAME "inomap"
/* reserve the first page for persistent state
*/
struct pers {
size64_t hnkcnt;
/* number of hunks in map
*/
size64_t segcnt;
/* number of segments
*/
xfs_ino_t last_ino_added;
};
typedef struct pers pers_t;
#define PERSSZ perssz
/* declarations of externally defined global symbols *************************/
extern size_t pgsz;
/* forward declarations of locally defined static functions ******************/
/* inomap primitives
*/
static intgen_t map_getset( xfs_ino_t, intgen_t, bool_t );
static intgen_t map_set( xfs_ino_t ino, intgen_t );
static seg_t * map_getsegment( xfs_ino_t ino );
/* definition of locally defined global variables ****************************/
/* definition of locally defined static variables *****************************/
static intgen_t pers_fd = -1;
/* file descriptor for persistent inomap backing store
*/
/* context for inomap construction - initialized by inomap_restore_pers
*/
static u_int64_t hnkcnt;
static u_int64_t segcnt;
static hnk_t *roothnkp = 0;
static hnk_t *tailhnkp;
static seg_t *lastsegp;
static xfs_ino_t last_ino_added;
/* map context and operators
*/
static inline void
SEG_SET_BITS( seg_t *segp, xfs_ino_t ino, intgen_t state )
{
register xfs_ino_t relino;
register u_int64_t mask;
register u_int64_t clrmask;
relino = ino - segp->base;
mask = ( u_int64_t )1 << relino;
clrmask = ~mask;
switch( state ) {
case 0:
segp->lobits &= clrmask;
segp->mebits &= clrmask;
segp->hibits &= clrmask;
break;
case 1:
segp->lobits |= mask;
segp->mebits &= clrmask;
segp->hibits &= clrmask;
break;
case 2:
segp->lobits &= clrmask;
segp->mebits |= mask;
segp->hibits &= clrmask;
break;
case 3:
segp->lobits |= mask;
segp->mebits |= mask;
segp->hibits &= clrmask;
break;
case 4:
segp->lobits &= clrmask;
segp->mebits &= clrmask;
segp->hibits |= mask;
break;
case 5:
segp->lobits |= mask;
segp->mebits &= clrmask;
segp->hibits |= mask;
break;
case 6:
segp->lobits &= clrmask;
segp->mebits |= mask;
segp->hibits |= mask;
break;
case 7:
segp->lobits |= mask;
segp->mebits |= mask;
segp->hibits |= mask;
break;
}
}
static inline intgen_t
SEG_GET_BITS( seg_t *segp, xfs_ino_t ino )
{
intgen_t state;
register xfs_ino_t relino;
register u_int64_t mask;
relino = ino - segp->base;
mask = ( u_int64_t )1 << relino;
if ( segp->lobits & mask ) {
state = 1;
} else {
state = 0;
}
if ( segp->mebits & mask ) {
state |= 2;
}
if ( segp->hibits & mask ) {
state |= 4;
}
return state;
}
/* definition of locally defined global functions ****************************/
rv_t
inomap_restore_pers( drive_t *drivep,
content_inode_hdr_t *scrhdrp,
char *hkdir )
{
drive_ops_t *dop = drivep->d_opsp;
char *perspath;
pers_t *persp;
hnk_t *pershnkp;
hnk_t *tmphnkp;
intgen_t fd;
/* REFERENCED */
intgen_t nread;
intgen_t rval;
/* REFERENCED */
intgen_t rval1;
int i;
bool_t ok;
/* sanity checks
*/
ASSERT( INOPERSEG == ( sizeof( (( seg_t * )0 )->lobits ) * NBBY ));
ASSERT( sizeof( hnk_t ) == HNKSZ );
ASSERT( sizeof( pers_t ) <= PERSSZ );
/* get inomap info from media hdr
*/
hnkcnt = scrhdrp->cih_inomap_hnkcnt;
segcnt = scrhdrp->cih_inomap_segcnt;
last_ino_added = scrhdrp->cih_inomap_lastino;
/* truncate and open the backing store
*/
perspath = open_pathalloc( hkdir, PERS_NAME, 0 );
( void )unlink( perspath );
fd = open( perspath,
O_RDWR | O_CREAT,
S_IRUSR | S_IWUSR );
if ( fd < 0 ) {
mlog( MLOG_NORMAL | MLOG_ERROR, _(
"could not open %s: %s\n"),
perspath,
strerror( errno ));
return RV_ERROR;
}
/* mmap the persistent hdr and space for the map
*/
persp = ( pers_t * ) mmap_autogrow(
PERSSZ
+
sizeof( hnk_t ) * ( size_t )hnkcnt,
fd,
( off64_t )0 );
if ( persp == ( pers_t * )-1 ) {
mlog( MLOG_NORMAL | MLOG_ERROR, _(
"unable to map %s: %s\n"),
perspath,
strerror( errno ));
return RV_ERROR;
}
/* load the pers hdr
*/
persp->hnkcnt = hnkcnt;
persp->segcnt = segcnt;
persp->last_ino_added = last_ino_added;
tmphnkp = ( hnk_t * )calloc( ( size_t )hnkcnt, sizeof( hnk_t ));
ASSERT( tmphnkp );
/* read the map in from media
*/
nread = read_buf( ( char * )tmphnkp,
sizeof( hnk_t ) * ( size_t )hnkcnt,
( void * )drivep,
( rfp_t )dop->do_read,
( rrbfp_t )dop->do_return_read_buf,
&rval );
pershnkp = (hnk_t *)((char *)persp + PERSSZ);
for(i = 0; i < hnkcnt; i++) {
xlate_hnk(&tmphnkp[i], &pershnkp[i], 1);
}
free(tmphnkp);
mlog(MLOG_NITTY, "inomap_restore_pers: pre-munmap\n");
/* close up
*/
rval1 = munmap( ( void * )persp,
PERSSZ
+
sizeof( hnk_t ) * ( size_t )hnkcnt );
ASSERT( ! rval1 );
( void )close( fd );
free( ( void * )perspath );
mlog(MLOG_NITTY, "inomap_restore_pers: post-munmap\n");
/* check the return code from read
*/
switch( rval ) {
case 0:
ASSERT( ( size_t )nread == sizeof( hnk_t ) * ( size_t )hnkcnt );
ok = inomap_sync_pers( hkdir );
if ( ! ok ) {
return RV_ERROR;
}
return RV_OK;
case DRIVE_ERROR_EOD:
case DRIVE_ERROR_EOF:
case DRIVE_ERROR_EOM:
case DRIVE_ERROR_MEDIA:
case DRIVE_ERROR_CORRUPTION:
return RV_CORRUPT;
case DRIVE_ERROR_DEVICE:
return RV_DRIVE;
case DRIVE_ERROR_CORE:
default:
return RV_CORE;
}
}
/* peels inomap from media
*/
rv_t
inomap_discard( drive_t *drivep, content_inode_hdr_t *scrhdrp )
{
drive_ops_t *dop = drivep->d_opsp;
u_int64_t tmphnkcnt;
/* REFERENCED */
intgen_t nread;
intgen_t rval;
/* get inomap info from media hdr
*/
tmphnkcnt = scrhdrp->cih_inomap_hnkcnt;
/* read the map in from media
*/
nread = read_buf( 0,
sizeof( hnk_t ) * ( size_t )tmphnkcnt,
( void * )drivep,
( rfp_t )dop->do_read,
( rrbfp_t )dop->do_return_read_buf,
&rval );
/* check the return code from read
*/
switch( rval ) {
case 0:
ASSERT( ( size_t )nread == sizeof( hnk_t ) * ( size_t )hnkcnt );
return RV_OK;
case DRIVE_ERROR_EOD:
case DRIVE_ERROR_EOF:
case DRIVE_ERROR_EOM:
case DRIVE_ERROR_MEDIA:
case DRIVE_ERROR_CORRUPTION:
return RV_CORRUPT;
case DRIVE_ERROR_DEVICE:
return RV_DRIVE;
case DRIVE_ERROR_CORE:
default:
return RV_CORE;
}
}
bool_t
inomap_sync_pers( char *hkdir )
{
char *perspath;
pers_t *persp;
hnk_t *hnkp;
/* sanity checks
*/
ASSERT( sizeof( hnk_t ) == HNKSZ );
/* only needed once per session
*/
if ( pers_fd >= 0 ) {
return BOOL_TRUE;
}
/* open the backing store. if not present, ok, hasn't been created yet
*/
perspath = open_pathalloc( hkdir, PERS_NAME, 0 );
pers_fd = open( perspath, O_RDWR );
if ( pers_fd < 0 ) {
return BOOL_TRUE;
}
/* mmap the persistent hdr
*/
persp = ( pers_t * ) mmap_autogrow(
PERSSZ,
pers_fd,
( off64_t )0 );
if ( persp == ( pers_t * )-1 ) {
mlog( MLOG_NORMAL | MLOG_ERROR, _(
"unable to map %s hdr: %s\n"),
perspath,
strerror( errno ));
return BOOL_FALSE;
}
/* read the pers hdr
*/
hnkcnt = persp->hnkcnt;
segcnt = persp->segcnt;
last_ino_added = persp->last_ino_added;
/* mmap the pers inomap
*/
ASSERT( hnkcnt * sizeof( hnk_t ) <= ( size64_t )INT32MAX );
roothnkp = ( hnk_t * ) mmap_autogrow(
sizeof( hnk_t ) * ( size_t )hnkcnt,
pers_fd,
( off64_t )PERSSZ );
if ( roothnkp == ( hnk_t * )-1 ) {
mlog( MLOG_NORMAL | MLOG_ERROR, _(
"unable to map %s: %s\n"),
perspath,
strerror( errno ));
return BOOL_FALSE;
}
/* correct the next pointers
*/
for ( hnkp = roothnkp
;
hnkp < roothnkp + ( intgen_t )hnkcnt - 1
;
hnkp++ ) {
hnkp->nextp = hnkp + 1;
}
hnkp->nextp = 0;
/* calculate the tail pointers
*/
tailhnkp = hnkp;
ASSERT( hnkcnt > 0 );
lastsegp = &tailhnkp->seg[ ( intgen_t )( segcnt
-
SEGPERHNK * ( hnkcnt - 1 )
-
1 ) ];
/* now all inomap operators will work
*/
return BOOL_TRUE;
}
/* de-allocate the persistent inomap
*/
void
inomap_del_pers( char *hkdir )
{
char *perspath = open_pathalloc( hkdir, PERS_NAME, 0 );
( void )unlink( perspath );
free( ( void * )perspath );
}
/* mark all included non-dirs as MAP_NDR_NOREST
*/
void
inomap_sanitize( void )
{
hnk_t *hnkp;
seg_t *segp;
/* step through all hunks, segs, and inos
*/
for ( hnkp = roothnkp
;
hnkp != 0
;
hnkp = hnkp->nextp ) {
for ( segp = hnkp->seg
;
segp < hnkp->seg + SEGPERHNK
;
segp++ ) {
xfs_ino_t ino;
if ( hnkp == tailhnkp && segp > lastsegp ) {
return;
}
for ( ino = segp->base
;
ino < segp->base + INOPERSEG
;
ino++ ) {
intgen_t state;
if ( ino > last_ino_added ) {
return;
}
state = SEG_GET_BITS( segp, ino );
if ( state == MAP_NDR_CHANGE ) {
state = MAP_NDR_NOREST;
SEG_SET_BITS( segp, ino, state );
}
}
}
}
}
/* called to mark a non-dir ino as TO be restored
*/
void
inomap_rst_add( xfs_ino_t ino )
{
ASSERT( pers_fd >= 0 );
( void )map_set( ino, MAP_NDR_CHANGE );
}
/* called to mark a non-dir ino as NOT to be restored
*/
void
inomap_rst_del( xfs_ino_t ino )
{
ASSERT( pers_fd >= 0 );
( void )map_set( ino, MAP_NDR_NOREST );
}
/* called to ask if any inos in the given range need to be restored.
* range is inclusive
*/
bool_t
inomap_rst_needed( xfs_ino_t firstino, xfs_ino_t lastino )
{
hnk_t *hnkp;
seg_t *segp;
/* if inomap not restored/resynced, just say yes
*/
if ( ! roothnkp ) {
return BOOL_TRUE;
}
/* may be completely out of range
*/
if ( firstino > last_ino_added ) {
return BOOL_FALSE;
}
/* find the hunk/seg containing first ino or any ino beyond
*/
for ( hnkp = roothnkp ; hnkp != 0 ; hnkp = hnkp->nextp ) {
if ( firstino > hnkp->maxino ) {
continue;
}
for ( segp = hnkp->seg; segp < hnkp->seg + SEGPERHNK ; segp++ ){
if ( hnkp == tailhnkp && segp > lastsegp ) {
return BOOL_FALSE;
}
if ( firstino < segp->base + INOPERSEG ) {
goto begin;
}
}
}
return BOOL_FALSE;
begin:
/* search until at least one ino is needed or until beyond last ino
*/
for ( ; ; ) {
xfs_ino_t ino;
if ( segp->base > lastino ) {
return BOOL_FALSE;
}
for ( ino = segp->base ; ino < segp->base + INOPERSEG ; ino++ ){
intgen_t state;
if ( ino < firstino ) {
continue;
}
if ( ino > lastino ) {
return BOOL_FALSE;
}
state = SEG_GET_BITS( segp, ino );
if ( state == MAP_NDR_CHANGE ) {
return BOOL_TRUE;
}
}
segp++;
if ( hnkp == tailhnkp && segp > lastsegp ) {
return BOOL_FALSE;
}
if ( segp >= hnkp->seg + SEGPERHNK ) {
hnkp = hnkp->nextp;
if ( ! hnkp ) {
return BOOL_FALSE;
}
segp = hnkp->seg;
}
}
/* NOTREACHED */
}
/* calls the callback for all inos with an inomap state included
* in the state mask. stops iteration when inos exhaused or cb
* returns FALSE.
*/
void
inomap_cbiter( intgen_t statemask,
bool_t ( * cbfunc )( void *ctxp, xfs_ino_t ino ),
void *ctxp )
{
hnk_t *hnkp;
seg_t *segp;
/* step through all hunks, segs, and inos
*/
for ( hnkp = roothnkp
;
hnkp != 0
;
hnkp = hnkp->nextp ) {
for ( segp = hnkp->seg
;
segp < hnkp->seg + SEGPERHNK
;
segp++ ) {
xfs_ino_t ino;
if ( hnkp == tailhnkp && segp > lastsegp ) {
return;
}
for ( ino = segp->base
;
ino < segp->base + INOPERSEG
;
ino++ ) {
intgen_t state;
if ( ino > last_ino_added ) {
return;
}
state = SEG_GET_BITS( segp, ino );
if ( statemask & ( 1 << state )) {
bool_t ok;
ok = ( cbfunc )( ctxp, ino );
if ( ! ok ) {
return;
}
}
}
}
}
}
/* definition of locally defined static functions ****************************/
/* map_getset - locates and gets the state of the specified ino,
* and optionally sets the state to a new value.
*/
static intgen_t
map_getset( xfs_ino_t ino, intgen_t newstate, bool_t setflag )
{
intgen_t state;
seg_t *segp;
if ((segp = map_getsegment( ino )) == NULL) {
return MAP_INO_UNUSED;
}
state = SEG_GET_BITS( segp, ino );
if ( setflag ) {
SEG_SET_BITS( segp, ino, newstate );
}
return state;
}
static seg_t *
map_getsegment( xfs_ino_t ino )
{
u_int64_t min;
u_int64_t max;
u_int64_t hnk;
u_int64_t seg;
/* Use binary search to find the hunk that contains the inode number,
* if any. This counts on the fact that all the hunks are contiguous
* in memory and therefore can be treated as an array instead of a
* list.
*/
min = 0;
max = hnkcnt - 1;
while (max >= min) {
hnk = min + ((max - min) / 2);
if (roothnkp[hnk].seg[0].base > ino) {
max = hnk - 1; /* in a lower hunk */
} else if (roothnkp[hnk].maxino < ino) {
min = hnk + 1; /* in a higher hunk */
} else {
break; /* we have a winner! */
}
}
if (max < min) {
return NULL; /* inode number fell between hunks */
}
/* Use binary search to find the segment within the hunk that contains
* the inode number, if any.
*/
min = 0;
if (hnk == hnkcnt - 1) {
max = segcnt - SEGPERHNK * ( hnkcnt - 1 ) - 1;
} else {
max = SEGPERHNK - 1;
}
while (max >= min) {
seg = min + ((max - min) / 2);
if (roothnkp[hnk].seg[seg].base > ino) {
max = seg - 1; /* in a lower segment */
} else if (roothnkp[hnk].seg[seg].base + INOPERSEG <= ino) {
min = seg + 1; /* in a higher segment */
} else {
return &roothnkp[hnk].seg[seg]; /* we have a winner! */
}
}
return NULL;
}
static intgen_t
map_set( xfs_ino_t ino, intgen_t state )
{
intgen_t oldstate;
oldstate = map_getset( ino, state, BOOL_TRUE );
return oldstate;
}