blob: 7e6127fcba92b599267a2afbb0c16aafbdbfdde6 [file] [log] [blame]
/*
* Copyright (c) 2000-2002 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 <sys/ioctl.h>
#include <utime.h>
#include <limits.h>
#include <time.h>
#include <xfs/handle.h>
#include <dirent.h>
#include <sys/ioctl.h>
#include "types.h"
#include "exit.h"
#include "util.h"
#include "cldmgr.h"
#include "path.h"
#include "openutil.h"
#include "getopt.h"
#include "stream.h"
#include "mlog.h"
#include "dlog.h"
#include "global.h"
#include "drive.h"
#include "media.h"
#include "content.h"
#include "content_inode.h"
#include "inomap.h"
#include "namreg.h"
#include "dirattr.h"
#include "bag.h"
#include "node.h"
#include "tree.h"
#include "libgen.h"
#include "mmap.h"
#include "exit.h"
/* structure definitions used locally ****************************************/
/* name of persistent state file
*/
#define PERS_NAME "tree"
/* orphanage specifics. ino must be otherwise unused in the dump source fs.
* zero works.
*/
#define ORPH_INO 0
#define ORPH_NAME "orphanage"
/* VM budgeting - give hash array one sixteenth, rest goes to node array
*/
#define HASHSZ_PERVM 16
/* reserve the first page for persistent state
*/
struct treePersStorage {
xfs_ino_t p_rootino;
/* ino of root
*/
nh_t p_rooth;
/* handle of root node
*/
nh_t p_orphh;
/* handle to orphanage node
*/
size64_t p_hashsz;
/* size of hash array (private to hash abstraction)
*/
size_t p_hashmask;
/* hash mask (private to hash abstraction)
*/
bool_t p_ownerpr;
/* restore directory owner/group attributes
*/
bool_t p_fullpr;
/* restoring a full level 0 non-resumed dump (can skip
* some steps)
*/
bool_t p_ignoreorphpr;
/* set if positive subtree or interactive
*/
bool_t p_restoredmpr;
/* restore DMI event settings
*/
bool_t p_truncategenpr;
/* truncate inode generation number (for compatibility
* with xfsdump format 2 and earlier)
*/
};
typedef struct treePersStorage treepers_t;
#define PERSSZ perssz
/* interactive dialog transient state
*/
#define INTER_ARGMAX 10 /* max number of args to interactive cmds */
struct inter {
size_t i_argc;
char *i_argv[ INTER_ARGMAX ];
nh_t i_cwdh;
char i_name[ NAME_MAX + 1 ];
};
typedef struct inter inter_t;
/* transient state
*/
struct tran {
bool_t t_toconlypr;
/* just display table of contents; don't restore files
*/
char *t_hkdir;
/* full absolute pathname of housekeeping directory
*/
char *t_dstdir;
/* full absolute pathname of destination directory
*/
bool_t t_dstdirisxfspr;
/* destination directory is an xfs filesystem; xfs-specific
* calls can be made when needed.
*/
char *t_orphdir;
/* full absolute pathname of orphanage directory
*/
char *t_hksubtree;
/* if non-NULL, is path of hkdir relative to dstdir.
* don't restore there.
*/
intgen_t t_persfd;
/* file descriptor of the persistent state file
*/
nh_t *t_hashp;
/* pointer to mapped hash array (private to hash abstraction)
*/
char t_namebuf[ NAME_MAX + 1 ];
/* to hold names temporarily retrieved from name registry
*/
inter_t t_inter;
/* context for interactive subtree selection
*/
};
typedef struct tran tran_t;
/* node structure. each node represents a directory entry
*/
#define NODESZ 56
struct node {
xfs_ino_t n_ino; /* 8 8 ino */
nrh_t n_nrh; /* 8 16 handle to name in name registry */
dah_t n_dah; /* 4 20 handle to directory attributes */
nh_t n_hashh; /* 4 24 hash array */
nh_t n_parh; /* 4 28 parent */
nh_t n_sibh; /* 4 32 sibling list */
nh_t n_sibprevh; /* 4 36 prev sibling list - dbl link list */
nh_t n_cldh; /* 4 40 children list */
nh_t n_lnkh; /* 4 44 hard link list */
gen_t n_gen; /* 4 48 generation count mod 0x10000 */
u_char_t n_flags; /* 1 49 action and state flags */
u_char_t n_nodehkbyte; /* 1 50 given to node abstraction */
char n_pad[6]; /* 6 56 */
};
typedef struct node node_t;
#define NF_REAL ( 1 << 0 )
/* set when the corresponding file/dir has been created in
* the restore destination.
*/
#define NF_SUBTREE ( 1 << 1 )
/* marks nodes in the selected subtrees.
*/
#define NF_REFED ( 1 << 2 )
/* indicates node is still referenced according to incremental/resumed
* dump. used to detect dirents no longer used. prior to restoring
* a dump session, this flag is cleared in all nodes. during the dirent
* restoral, it is set. it may also be set during the adjustment
* for referenced but undumped directories. NOTE: nodes in the
* orphanage NEVER have this flag set.
*/
#define NF_WRITTEN ( 1 << 3 )
/* set as soon as a non-dir node restore is begun. allows
* overwrite inhibit options to work with segmented files
*/
#define NF_ISDIR ( 1 << 4 )
/* indicates this node is a directory. set when a directory is taken
* from the dirdump.
*/
#define NF_DUMPEDDIR ( 1 << 5 )
/* indicates this node is a directory present in the current dirdump.
* at beginning of session, this flag is cleared in all nodes.
* then as each directory dump is read from media, the flag
* is set from the corresponding node. this allows adjustments to
* the NF_REFED flag: if a directory was not dumped, either it no
* longer exists or it has not changed. if it is referenced, we assume
* it exists, in which case if it is not dumped then all of its entries
* are referenced as well.
*/
#define NF_NEWORPH ( 1 << 6 )
/* cleared from all nodes in the orphanage before a dump is applied.
* set if a dir is seen in the dirdump but no node exists for it.
* cleared if that dir is adopted subsequently during the dirdump.
* set if a nondir is seen in the nondir dump but no node exists for
* it. in either case a node is created and placed in the orphanage.
* during rmdir/unlink processing, nodes so marked are left alone.
* since the flag is cleared at the beginning of the next increment,
* old orphans had better be adopted, otherwise they will be unlinked.
*/
/* link list iterator context
*/
struct link_iter_context {
nh_t li_headh; /* head of hard link list */
nh_t li_prevh; /* next to last node returned by _next() */
nh_t li_lasth; /* last node returned by _next() */
bool_t li_donepr; /* set as soon as last.next null */
};
typedef struct link_iter_context link_iter_context_t;
/* used for caching parent pathname from previous Node2path result
*/
struct path_cache {
nh_t nh;
intgen_t len;
char buf[MAXPATHLEN];
};
typedef struct path_cache path_cache_t;
/* declarations of externally defined global symbols *************************/
extern void usage( void );
extern size_t pgsz;
extern size_t pgmask;
extern bool_t restore_rootdir_permissions;
/* forward declarations of locally defined static functions ******************/
static nh_t Node_alloc( xfs_ino_t ino,
gen_t gen,
nrh_t nrh,
dah_t dah,
size_t flags );
static void Node_free( nh_t *nhp );
static node_t * Node_map( nh_t nh );
static void Node_unmap( nh_t nh, node_t **npp );
static intgen_t Node2path_recurse( nh_t nh, char *buf,
intgen_t bufsz, intgen_t level );
static void adopt( nh_t parh, nh_t cldh, nrh_t nrh );
static nrh_t disown( nh_t cldh );
static void selsubtree( nh_t nh, bool_t sensepr );
static void selsubtree_recurse_down( nh_t nh, bool_t sensepr );
static nh_t link_hardh( xfs_ino_t ino, gen_t gen );
static nh_t link_nexth( nh_t nh );
static nh_t link_matchh( nh_t hardh, nh_t parh, char *name );
static void link_in( nh_t nh );
static void link_out( nh_t nh );
static void link_headiter( bool_t ( * cbfp )( void *contextp, nh_t hardh ),
void *contextp );
static void link_iter_init( link_iter_context_t *link_iter_contextp,
nh_t hardheadh );
static nh_t link_iter_next( link_iter_context_t *link_iter_contextp );
void link_iter_unlink( link_iter_context_t *link_iter_contextp, nh_t nh );
static bool_t hash_init( size64_t vmsz,
size64_t dircnt,
size64_t nondircnt,
char *perspath );
static bool_t hash_sync( char *perspath );
static inline size_t hash_val(xfs_ino_t ino, size_t hashmask);
static void hash_in( nh_t nh );
static void hash_out( nh_t nh );
static nh_t hash_find( xfs_ino_t ino, gen_t gen );
static void hash_iter( bool_t ( * cbfp )( void *contextp, nh_t hashh ),
void *contextp );
static void setdirattr( dah_t dah, char *path );
static bool_t tsi_walkpath( char *arg, nh_t rooth, nh_t cwdh,
dlog_pcbp_t pcb, void *pctxp,
nh_t *namedhp, nh_t *parhp, nh_t *cldhp,
xfs_ino_t *inop, bool_t *isdirprp, bool_t *isselpr );
static bool_t Node2path( nh_t nh, char *path, char *errmsg );
static bool_t tree_setattr_recurse( nh_t parh, char *path );
static void tsi_cmd_pwd_recurse( void *ctxp,
dlog_pcbp_t pcb,
void *pctxp,
nh_t nh );
static int mkdir_r(char *path);
#ifdef TREE_CHK
static bool_t Node_chk( nh_t nh, nh_t *nexthashhp, nh_t *nextlnkhp );
static bool_t tree_chk2( void );
#endif /* TREE_CHK */
/* definition of locally defined global variables ****************************/
/* definition of locally defined static variables *****************************/
static treepers_t *persp = 0;
static tran_t *tranp = 0;
static char *persname = PERS_NAME;
static char *orphname = ORPH_NAME;
static xfs_ino_t orphino = ORPH_INO;
/* definition of locally defined global functions ****************************/
/* ARGSUSED */
bool_t
tree_init( char *hkdir,
char *dstdir,
bool_t toconlypr,
bool_t ownerpr,
xfs_ino_t rootino,
xfs_ino_t firstino,
xfs_ino_t lastino,
size64_t dircnt,
size64_t nondircnt,
size64_t vmsz,
bool_t fullpr,
bool_t restoredmpr,
bool_t dstdirisxfspr,
u_int32_t dumpformat,
bool_t truncategenpr )
{
off64_t nodeoff;
char *perspath;
bool_t ok;
intgen_t rval;
/* sanity checks
*/
ASSERT( ! ( PERSSZ % pgsz ));
ASSERT( sizeof( persp ) <= PERSSZ );
ASSERT( sizeof( node_t ) <= NODESZ );
ASSERT( ! persp );
ASSERT( ! tranp );
/* allocate transient state
*/
tranp = ( tran_t * )calloc( 1, sizeof( tran_t ));
ASSERT( tranp );
tranp->t_toconlypr = toconlypr;
tranp->t_hkdir = hkdir;
tranp->t_dstdir = dstdir;
tranp->t_dstdirisxfspr = dstdirisxfspr;
/* allocate a char string buffer to hold the abs. pathname
* of the orphanage directory file. load it with the pathname.
*/
tranp->t_orphdir = open_pathalloc( tranp->t_dstdir,
orphname,
0 );
/* determine if housekeeping dir is within the destination.
* generate a relative path containing the difference,
* else null. will not restore into there.
*/
if ( strcmp( dstdir, "." )) {
tranp->t_hksubtree = path_diff( hkdir, dstdir );
} else {
tranp->t_hksubtree = 0;
}
/* create an orphanage, if it already exists, complain.
* not needed if just a table-of-contents restore.
*/
if ( ! tranp->t_toconlypr ) {
rval = mkdir( tranp->t_orphdir, S_IRWXU );
if ( rval ) {
if ( errno == EEXIST ) {
mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_TREE, _(
"%s already exists: "
"rm -rf prior to initating restore\n"),
tranp->t_orphdir );
} else {
mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_TREE, _(
"unable to create %s: %s\n"),
tranp->t_orphdir,
strerror( errno ));
}
return BOOL_FALSE;
}
}
/* build a full pathname to pers. state file
*/
perspath = open_pathalloc( tranp->t_hkdir, persname, 0 );
/* create the persistent state file
*/
( void )unlink( perspath );
tranp->t_persfd = open( perspath,
O_RDWR | O_CREAT,
S_IRUSR | S_IWUSR );
if ( tranp->t_persfd < 0 ) {
mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_TREE, _(
"could not open %s: %s\n"),
perspath,
strerror( errno ));
return BOOL_FALSE;
}
/* mmap the persistent state
*/
ASSERT( ! ( PERSSZ % pgsz ));
persp = ( treepers_t * ) mmap_autogrow(
PERSSZ,
tranp->t_persfd,
( off64_t )0 );
if ( persp == ( treepers_t * )-1 ) {
mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_TREE, _(
"unable to map %s: %s\n"),
perspath,
strerror( errno ));
return BOOL_FALSE;
}
/* create the hash abstraction. it will map more of the
* persistent state file.
*/
ok = hash_init( vmsz / HASHSZ_PERVM, dircnt, nondircnt, perspath );
if ( ! ok ) {
return BOOL_FALSE;
}
/* initialize the node abstraction. let it's use of backing store
* begin immediately after the hash abstraction. give it the remainder
* of vm.
*/
ASSERT( persp->p_hashsz <= ( size64_t )( OFF64MAX - ( off64_t )PERSSZ));
nodeoff = ( off64_t )PERSSZ + ( off64_t )persp->p_hashsz;
ASSERT( vmsz > ( size64_t )nodeoff );
ok = node_init( tranp->t_persfd,
nodeoff,
NODESZ,
( ix_t )offsetofmember( node_t, n_nodehkbyte ),
sizeof( size64_t ), /* node alignment */
vmsz - ( size64_t )nodeoff,
dircnt + nondircnt );
if ( ! ok ) {
return BOOL_FALSE;
}
/* extract the root ino and allocate a node for
* the root and for the orphanage. place both nodes
* in the hash list. make the orphanage a child of
* root, and indicate he is real.
*
* Note that we assume that the root inode always
* has a generation count of zero - which is true.
*/
persp->p_rootino = rootino;
persp->p_rooth = Node_alloc( rootino,
0, /* gen cnt */
NRH_NULL,
DAH_NULL,
NF_ISDIR | NF_REAL );
if (persp->p_rooth == NH_NULL)
return BOOL_FALSE;
link_in( persp->p_rooth );
persp->p_orphh = Node_alloc( orphino,
0, /* gen cnt */
namreg_add( orphname, strlen( orphname )),
DAH_NULL,
NF_ISDIR | NF_REAL );
if (persp->p_orphh == NH_NULL)
return BOOL_FALSE;
link_in( persp->p_orphh );
adopt( persp->p_rooth, persp->p_orphh, NRH_NULL );
/* record if we should attempt to restore original owner/group
*/
persp->p_ownerpr = ownerpr;
/* record if this is a full dump. can skip link processing if so
*/
persp->p_fullpr = fullpr;
/* record if DMI event settings should be restored
*/
persp->p_restoredmpr = restoredmpr;
/* record if truncated generation numbers are required
*/
if ( dumpformat < GLOBAL_HDR_VERSION_3 ) {
persp->p_truncategenpr = BOOL_TRUE;
mlog( MLOG_NORMAL | MLOG_DEBUG | MLOG_TREE, _(
"dump format version %u used truncated inode generation numbers\n"),
dumpformat );
} else if ( truncategenpr ) {
persp->p_truncategenpr = BOOL_TRUE;
mlog( MLOG_NORMAL | MLOG_DEBUG | MLOG_TREE, _(
"forcing use of truncated inode generation numbers\n"));
} else {
persp->p_truncategenpr = BOOL_FALSE;
}
return BOOL_TRUE;
}
bool_t
tree_sync( char *hkdir,
char *dstdir,
bool_t toconlypr,
bool_t fullpr,
bool_t dstdirisxfspr )
{
off64_t nodeoff;
char *perspath;
bool_t ok;
intgen_t rval;
if ( persp ) {
return BOOL_TRUE;
}
/* sanity checks
*/
ASSERT( ! ( PERSSZ % pgsz ));
ASSERT( sizeof( persp ) <= PERSSZ );
ASSERT( sizeof( node_t ) <= NODESZ );
ASSERT( ! persp );
ASSERT( ! tranp );
/* allocate transient state
*/
tranp = ( tran_t * )calloc( 1, sizeof( tran_t ));
ASSERT( tranp );
tranp->t_toconlypr = toconlypr;
tranp->t_hkdir = hkdir;
tranp->t_dstdir = dstdir;
tranp->t_dstdirisxfspr = dstdirisxfspr;
/* allocate a char string buffer to hold the abs. pathname
* of the orphanage directory file. load it with the pathname.
*/
tranp->t_orphdir = open_pathalloc( tranp->t_dstdir,
orphname,
0 );
/* determine if housekeeping dir is within the destination.
* generate a relative path containing the difference,
* else null. will not restore into there.
*/
if ( strcmp( dstdir, "." )) {
tranp->t_hksubtree = path_diff( hkdir, dstdir );
} else {
tranp->t_hksubtree = 0;
}
/* re-create the orphanage (in case someone rmdir'ed it)
*/
rval = mkdir( tranp->t_orphdir, S_IRWXU );
if ( rval && errno != EEXIST ) {
mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_TREE, _(
"unable to recreate %s: %s\n"),
tranp->t_orphdir,
strerror( errno ));
return BOOL_FALSE;
}
/* build a full pathname to pers. state file
*/
perspath = open_pathalloc( tranp->t_hkdir, persname, 0 );
/* re-open the persistent state file
*/
tranp->t_persfd = open( perspath, O_RDWR );
if ( tranp->t_persfd < 0 ) {
mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_TREE, _(
"could not open %s: %s\n"),
perspath,
strerror( errno ));
return BOOL_FALSE;
}
/* mmap the persistent state
*/
ASSERT( ! ( PERSSZ % pgsz ));
persp = ( treepers_t * ) mmap_autogrow(
PERSSZ,
tranp->t_persfd,
( off64_t )0 );
if ( persp == ( treepers_t * )-1 ) {
mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_TREE, _(
"unable to map %s: %s\n"),
perspath,
strerror( errno ));
return BOOL_FALSE;
}
/* update the fullpr field of the persistent state to match
* the input of our caller.
*/
persp->p_fullpr = fullpr;
/* regardless of the format of this dump, if the previously applied
* dump used truncated generation numbers, then we need to as well.
*/
if ( persp->p_truncategenpr ) {
mlog( MLOG_NORMAL | MLOG_DEBUG | MLOG_TREE, _(
"using truncated inode generation numbers for "
"compatibility with previously applied restore\n") );
}
/* rsynchronize with the hash abstraction. it will map more of the
* persistent state file.
*/
ok = hash_sync( perspath );
if ( ! ok ) {
return BOOL_FALSE;
}
/* synchronize with the node abstraction.
*/
ASSERT( persp->p_hashsz <= ( size64_t )( OFF64MAX - ( off64_t )PERSSZ));
nodeoff = ( off64_t )PERSSZ + ( off64_t )persp->p_hashsz;
ok = node_sync( tranp->t_persfd, nodeoff );
if ( ! ok ) {
return BOOL_FALSE;
}
/* extract the root ino and allocate a node for
* the root and for the orphanage. place both nodes
* in the hash list. make the orphanage a child of
* root, and indicate he is real.
*/
return BOOL_TRUE;
}
bool_t
tree_check_dump_format( u_int32_t dumpformat )
{
if ( dumpformat < GLOBAL_HDR_VERSION_3 && !persp->p_truncategenpr ) {
mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_TREE, _(
"encountered dump format %d after a "
"restore of format %d or newer\n"),
dumpformat, GLOBAL_HDR_VERSION_3 );
mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_TREE, _(
"to restore this series of dumps, use the -%c "
"option on the first restore\n"),
GETOPT_FMT2COMPAT );
return BOOL_FALSE;
}
return BOOL_TRUE;
}
/* recursively descend the tree clearing REFED and DIRDUMPED and NEWORPH
* flags. force the orphanage to be refed and dumped, so we won't try
* to orphan it, and so things added to it won't look like they are
* referenced during ref adj. also null dirattr handles, since they are
* not retained across dump session restores.
*/
static void tree_marknoref_recurse( nh_t parh );
void
tree_marknoref( void )
{
tree_marknoref_recurse( persp->p_rooth );
{
node_t *orphp;
orphp = Node_map( persp->p_orphh );
orphp->n_flags |= ( NF_REFED | NF_DUMPEDDIR );
ASSERT( orphp->n_dah == DAH_NULL );
Node_unmap( persp->p_orphh, &orphp );
}
}
static void
tree_marknoref_recurse( nh_t parh )
{
nh_t cldh;
{
node_t *parp;
parp = Node_map( parh );
parp->n_flags &= ~( NF_REFED | NF_DUMPEDDIR | NF_NEWORPH );
parp->n_dah = DAH_NULL;
cldh = parp->n_cldh;
Node_unmap( parh, &parp );
}
while ( cldh != NH_NULL ) {
node_t *cldp;
nh_t nextcldh;
tree_marknoref_recurse( cldh ); /* RECURSION */
cldp = Node_map( cldh );
nextcldh = cldp->n_sibh;
Node_unmap( cldh, &cldp );
cldh = nextcldh;
}
}
void
tree_markallsubtree( bool_t sensepr )
{
if ( ! sensepr ) {
persp->p_ignoreorphpr = BOOL_TRUE;
}
selsubtree( persp->p_rooth, sensepr );
}
nh_t
tree_begindir( filehdr_t *fhdrp, dah_t *dahp )
{
nh_t hardh;
xfs_ino_t ino = fhdrp->fh_stat.bs_ino;
gen_t gen = fhdrp->fh_stat.bs_gen;
dah_t dah;
if ( persp->p_truncategenpr ) {
gen = BIGGEN2GEN( gen );
}
/* sanity check - orphino is supposed to be an unused ino!
*/
ASSERT( ino != orphino );
/* lookup head of hardlink list
*/
hardh = link_hardh( ino, gen );
ASSERT( ino != persp->p_rootino || hardh == persp->p_rooth );
/* already present
*/
if ( hardh != NH_NULL ) {
node_t *hardp;
hardp = Node_map( hardh );
if ( ! ( hardp->n_flags & NF_ISDIR )) {
/* case 1: previously seen as dirent, now know is dir
*/
mlog( MLOG_TRACE | MLOG_TREE,
"directory %llu %u (%u): "
"upgrading to dir\n",
ino,
gen,
fhdrp->fh_stat.bs_gen );
if ( ! tranp->t_toconlypr ) {
ASSERT( hardp->n_dah == DAH_NULL );
hardp->n_dah = dirattr_add( fhdrp );
}
} else if ( ! tranp->t_toconlypr && hardp->n_dah == DAH_NULL ) {
/* case 2: node is a dir, but had thrown away dirattr
*/
mlog( MLOG_TRACE | MLOG_TREE,
"directory %llu %u (%u): "
"updating\n",
ino,
gen,
fhdrp->fh_stat.bs_gen );
hardp->n_dah = dirattr_add( fhdrp );
} else {
/* case 3: already has dirattr; must be restart
*/
mlog( MLOG_TRACE | MLOG_TREE,
"directory %llu %u (%u): "
"retaining\n",
ino,
gen,
fhdrp->fh_stat.bs_gen );
}
hardp->n_flags |= NF_ISDIR;
hardp->n_flags |= NF_DUMPEDDIR;
*dahp = hardp->n_dah;
Node_unmap( hardh, &hardp );
} else {
/* case 4: first time seen
*/
mlog( MLOG_TRACE | MLOG_TREE,
"directory %llu %u (%u): "
"new\n",
ino,
gen,
fhdrp->fh_stat.bs_gen );
if ( ! tranp->t_toconlypr ) {
dah = dirattr_add( fhdrp );
} else {
dah = DAH_NULL;
}
hardh = Node_alloc( ino,
gen,
NRH_NULL,
dah,
NF_ISDIR | NF_NEWORPH );
if (hardh == NH_NULL)
return NH_NULL;
link_in( hardh );
adopt( persp->p_orphh, hardh, NRH_NULL );
*dahp = dah;
}
return hardh;
}
rv_t
tree_addent( nh_t parh, xfs_ino_t ino, gen_t gen, char *name, size_t namelen )
{
nh_t hardh;
if ( persp->p_truncategenpr ) {
gen = BIGGEN2GEN( gen );
}
/* sanity check - orphino is supposed to be an unused ino!
*/
ASSERT( ino != orphino );
/* don't allow entries named "orphanage" under root to be added
*/
if ( parh == persp->p_rooth && !strcmp( name, orphname )) {
mlog( MLOG_DEBUG | MLOG_TREE,
"dirent %s %llu %u: "
"skipping (reserved)\n",
name,
ino,
gen );
return RV_OK;
}
/* if the parent is null, just ignore
*/
if ( parh == NH_NULL ) {
mlog( MLOG_DEBUG | MLOG_TREE,
"dirent %s %llu %u: "
"skipping (null parent)\n",
name,
ino,
gen );
return RV_OK;
}
/* see if one or more links to this ino already exist.
*/
hardh = link_hardh( ino, gen );
if ( hardh != NH_NULL ) {
node_t *hardp;
hardp = Node_map( hardh );
if ( hardp->n_flags & NF_ISDIR ) {
nh_t renameh;
node_t *renamep;
/* REFERENCED */
intgen_t namebuflen;
hardp->n_flags |= NF_REFED;
if ( hardp->n_parh == persp->p_orphh ) {
/* dir now seen as entry
* if in orph but REAL, must be pending rename
*/
if ( ( hardp->n_flags & NF_REAL )
&&
hardp->n_lnkh == NH_NULL ) {
hardp->n_lnkh = Node_alloc( ino,
gen,
NRH_NULL,
DAH_NULL,
0 );
if (hardp->n_lnkh == NH_NULL)
return RV_ERROR;
}
if ( hardp->n_lnkh != NH_NULL ) {
ASSERT( hardp->n_flags & NF_REAL );
renameh = hardp->n_lnkh;
renamep = Node_map( renameh );
if ( renamep->n_parh == NH_NULL ) {
mlog( MLOG_DEBUG | MLOG_TREE,
"dirent %s %llu %u: "
"adopting (dir par)\n",
name,
ino,
gen );
renamep->n_parh = parh;
}
if ( renamep->n_parh != parh ) {
mlog( MLOG_DEBUG | MLOG_TREE,
"dirent %s %llu %u: "
"re-adopting (dir par)\n",
name,
ino,
gen );
renamep->n_parh = parh;
}
if ( renamep->n_nrh != NRH_NULL ) {
namebuflen
=
namreg_get( renamep->n_nrh,
tranp->t_namebuf,
sizeof( tranp->t_namebuf ));
ASSERT( namebuflen > 0 );
if ( strcmp( name,
tranp->t_namebuf )) {
mlog( MLOG_DEBUG
|
MLOG_TREE,
"dirent %s "
"%llu %u: "
"re-adopting "
"(dir name): "
"was %s\n",
name,
ino,
gen,
tranp->t_namebuf );
namreg_del(
renamep->n_nrh );
renamep->n_nrh =
NRH_NULL;
}
}
if ( renamep->n_nrh == NRH_NULL ) {
renamep->n_nrh
=
namreg_add( name, namelen );
renamep->n_parh = parh;
}
Node_unmap( renameh, &renamep );
} else {
nrh_t nrh;
hardp->n_flags &= ~NF_NEWORPH;
ASSERT( hardp->n_nrh == NRH_NULL );
ASSERT( hardp->n_parh != NH_NULL );
nrh = disown( hardh );
ASSERT( nrh == NRH_NULL );
nrh = namreg_add( name, namelen );
adopt( parh, hardh, nrh );
mlog( MLOG_DEBUG | MLOG_TREE,
"dirent %s %llu %u: "
"updating (dir)\n",
name,
ino,
gen );
}
} else {
ASSERT( hardp->n_nrh != NRH_NULL );
namebuflen
=
namreg_get( hardp->n_nrh,
tranp->t_namebuf,
sizeof( tranp->t_namebuf ));
ASSERT( namebuflen > 0 );
if ( hardp->n_parh == parh
&&
! strcmp( tranp->t_namebuf, name )) {
/* dir seen as entry again
*/
if ( hardp->n_lnkh != NH_NULL ) {
/* rescind rename
*/
mlog( MLOG_DEBUG | MLOG_TREE,
"dirent %s %llu %u: "
"rescind rename (dir)\n",
name,
ino,
gen );
Node_free( &hardp->n_lnkh );
} else {
mlog( MLOG_DEBUG | MLOG_TREE,
"dirent %s %llu %u: "
"retaining (dir)\n",
name,
ino,
gen );
}
} else {
/* dir rename
*/
nh_t renameh;
node_t *renamep;
if ( hardp->n_lnkh == NH_NULL ) {
renameh = Node_alloc( ino,
gen,
NRH_NULL,
DAH_NULL,
0 );
if (renameh == NH_NULL)
return RV_ERROR;
hardp->n_lnkh = renameh;
} else {
mlog( MLOG_DEBUG | MLOG_TREE,
"dirent %s %llu %u: "
"re-renaming\n",
name,
ino,
gen );
renameh = hardp->n_lnkh;
renamep = Node_map( renameh );
renamep->n_parh = NH_NULL;
if ( renamep->n_nrh
!=
NRH_NULL ) {
namreg_del( renamep->n_nrh );
}
renamep->n_nrh = NRH_NULL;
Node_unmap( renameh, &renamep );
}
renamep = Node_map( renameh );
ASSERT( hardp->n_parh != NH_NULL );
if ( hardp->n_parh != parh ) {
/* different parent
*/
renamep->n_parh = parh;
mlog( MLOG_DEBUG | MLOG_TREE,
"dirent %s %llu %u: "
"renaming (parent)\n",
name,
ino,
gen );
}
if ( strcmp( tranp->t_namebuf, name )) {
/* different name
*/
renamep->n_nrh =
namreg_add( name, namelen );
mlog( MLOG_DEBUG | MLOG_TREE,
"dirent %s %llu %u: "
"renaming (name)\n",
name,
ino,
gen );
}
Node_unmap( renameh, &renamep );
}
}
} else {
nh_t matchh;
matchh = link_matchh( hardh, parh, name );
if ( matchh != NH_NULL ) {
/* entry already exists
*/
node_t *matchp;
matchp = Node_map( matchh );
matchp->n_flags |= NF_REFED;
Node_unmap( matchh, &matchp );
mlog( MLOG_DEBUG | MLOG_TREE,
"dirent %s %llu %u: "
"retaining (nondir)\n",
name,
ino,
gen );
} else {
/* case 5: new hard link
*/
nrh_t nrh;
nh_t linkh;
nrh = namreg_add( name, namelen );
linkh = Node_alloc( ino,
gen,
NRH_NULL,
DAH_NULL,
NF_REFED );
if (linkh == NH_NULL)
return RV_ERROR;
link_in( linkh );
adopt( parh, linkh, nrh );
mlog( MLOG_DEBUG | MLOG_TREE,
"dirent %s %llu %u: "
"adding (link)\n",
name,
ino,
gen );
}
}
Node_unmap( hardh, &hardp );
} else {
/* case 6: new entry
*/
nrh_t nrh;
nrh = namreg_add( name, namelen );
hardh = Node_alloc( ino,
gen,
NRH_NULL,
DAH_NULL,
NF_REFED );
if (hardh == NH_NULL)
return RV_ERROR;
link_in( hardh );
adopt( parh, hardh, nrh );
mlog( MLOG_DEBUG | MLOG_TREE,
"dirent %s %llu %u: "
"adding (new)\n",
name,
ino,
gen );
}
return RV_OK;
}
/* ARGSUSED */
void
tree_enddir( nh_t dirh )
{
}
bool_t
tree_subtree_parse( bool_t sensepr, char *path )
{
nh_t namedh;
nh_t parh;
nh_t cldh;
xfs_ino_t ino;
bool_t isdirpr;
bool_t isselpr;
bool_t ok;
/* walk the tree down this relative pathname from the root.
*/
ok = tsi_walkpath( path,
NH_NULL,
persp->p_rooth,
0,
0,
&namedh,
&parh,
&cldh,
&ino,
&isdirpr,
&isselpr );
if ( ! ok ) {
return BOOL_FALSE;
}
/* set or clear flag in this node and all of its children,
* and ajust parentage flags.
*/
selsubtree( namedh, sensepr );
return BOOL_TRUE;
}
/* tree_post - called after the dirdump has been applied.
* first phase is to eliminate all unreferenced dirents.
* done by recursive depth-wise descent of the tree. on the way
* up, unlink or orphan unreferenced nondirs, unlink unreferenced
* dirs, orphan dirs to be renamed. if a dir is unreferenced, but
* contains referenced dirents, orphan those dirents. orphan unreferenced
* nondirs if they are the last link to an inode referenced but not real
* somewhere else in the tree. next, make new directories. then rename
* directories. finally, create hardlinks from orphanage.
*/
static bool_t noref_elim_recurse( nh_t parh,
nh_t cldh,
bool_t parrefpr,
char *path1,
char *path2 );
static bool_t mkdirs_recurse( nh_t parh,
nh_t cldh,
char *path );
static bool_t rename_dirs( nh_t cldh,
char *path1,
char *path2 );
static bool_t proc_hardlinks( char *path1,
char *path2 );
bool_t
tree_post( char *path1, char *path2 )
{
node_t *rootp;
node_t *orphp;
nh_t cldh;
bool_t ok;
/* eliminate unreferenced dirents
*/
if ( ! persp->p_fullpr ) {
mlog( MLOG_DEBUG | MLOG_TREE,
"eliminating unreferenced directory entries\n" );
rootp = Node_map( persp->p_rooth );
cldh = rootp->n_cldh;
Node_unmap( persp->p_rooth, &rootp );
ok = noref_elim_recurse( persp->p_rooth,
cldh,
BOOL_TRUE,
path1,
path2 );
if ( ! ok ) {
return BOOL_FALSE;
}
}
/* make new directories
*/
mlog( MLOG_DEBUG | MLOG_TREE,
"making new directories\n" );
rootp = Node_map( persp->p_rooth );
cldh = rootp->n_cldh;
Node_unmap( persp->p_rooth, &rootp );
ok = mkdirs_recurse( persp->p_rooth, cldh, path1 );
if ( ! ok ) {
return BOOL_FALSE;
}
#ifdef TREE_CHK
ASSERT( tree_chk( ));
#endif /* TREE_CHK */
/* rename directories
*/
if ( ! persp->p_fullpr ) {
mlog( MLOG_DEBUG | MLOG_TREE,
"performing directory renames\n" );
orphp = Node_map( persp->p_orphh );
cldh = orphp->n_cldh;
Node_unmap( persp->p_orphh, &orphp );
ok = rename_dirs( cldh, path1, path2 );
if ( ! ok ) {
return BOOL_FALSE;
}
}
#ifdef TREE_CHK
ASSERT( tree_chk( ));
#endif /* TREE_CHK */
/* process hard links
*/
if ( ! persp->p_fullpr ) {
mlog( MLOG_DEBUG | MLOG_TREE,
"processing hard links\n" );
ok = proc_hardlinks( path1, path2 );
if ( ! ok ) {
return BOOL_FALSE;
}
}
return BOOL_TRUE;
}
/* ARGSUSED */
static bool_t
noref_elim_recurse( nh_t parh,
nh_t cldh,
bool_t parrefpr,
char *path1,
char *path2 )
{
while ( cldh != NH_NULL ) {
node_t *cldp;
xfs_ino_t ino;
gen_t gen;
bool_t inorphanagepr;
bool_t isdirpr;
bool_t isrealpr;
bool_t isrefpr;
bool_t isrenamepr;
nh_t renameh;
nh_t grandcldh;
nh_t nextcldh;
intgen_t rval;
bool_t ok;
cldp = Node_map( cldh );
ino = cldp->n_ino;
gen = cldp->n_gen;
inorphanagepr = cldp->n_parh == persp->p_orphh;
isdirpr = ( cldp->n_flags & NF_ISDIR );
isrealpr = ( cldp->n_flags & NF_REAL );
isrefpr = ( cldp->n_flags & NF_REFED );
isrenamepr = ( isdirpr && cldp->n_lnkh != NH_NULL );
renameh = cldp->n_lnkh;
grandcldh = cldp->n_cldh;
nextcldh = cldp->n_sibh;
#ifdef TREE_DEBUG
ok = Node2path( cldh, path1, _("noref debug") );
mlog( MLOG_TRACE | MLOG_TREE,
"noref: %s: isdir = %d, isreal = %d, isref = %d, "
"isrenamedir = %d\n",
path1, isdirpr, isrealpr, isrefpr, isrenamepr );
#endif
Node_unmap( cldh, &cldp );
if ( isdirpr ) {
bool_t ok;
ok = noref_elim_recurse( cldh,
grandcldh,
isrefpr,
path1,
path2 );
/* RECURSION */
if ( ! ok ) {
return BOOL_FALSE;
}
if ( inorphanagepr ) {
cldh = nextcldh;
continue;
}
if ( ! isrefpr ) {
nrh_t nrh;
ASSERT( ! isrenamepr );
if ( isrealpr ) {
ok = Node2path( cldh, path1, _("rmdir") );
if ( ! ok ) {
cldh = nextcldh;
continue;
}
mlog( MLOG_TRACE | MLOG_TREE,
"rmdir %s\n",
path1 );
rval = rmdir( path1 );
if ( rval ) {
mlog( MLOG_NORMAL
|
MLOG_WARNING
|
MLOG_TREE, _(
"unable to rmdir %s: %s\n"),
path1,
strerror( errno ));
cldh = nextcldh;
continue;
}
}
nrh = disown( cldh );
ASSERT( nrh != NRH_NULL );
namreg_del( nrh );
link_out( cldh );
Node_free( &cldh );
}
if ( isrenamepr ) {
nrh_t nrh;
node_t *renamep;
ASSERT( isrefpr );
ASSERT( isrealpr );
ok = Node2path( cldh,
path1,
_("tmp dir rename src") );
if ( ! ok ) {
cldh = nextcldh;
continue;
}
nrh = disown( cldh );
ASSERT( nrh != NRH_NULL );
adopt( persp->p_orphh, cldh, NRH_NULL );
ok = Node2path( cldh,
path2,
_("tmp dir rename dst") );
if ( ! ok ) {
/* REFERENCED */
nrh_t dummynrh;
dummynrh = disown( cldh );
ASSERT( dummynrh == NRH_NULL );
adopt( parh, cldh, nrh );
cldh = nextcldh;
continue;
}
mlog( MLOG_TRACE | MLOG_TREE,
"rename dir %s to %s\n",
path1,
path2 );
rval = rename( path1, path2 );
if ( rval ) {
/* REFERENCED */
nrh_t dummynrh;
mlog( MLOG_NORMAL | MLOG_WARNING, _(
"unable to rename dir %s "
"to dir %s: %s\n"),
path1,
path2,
strerror( errno ));
dummynrh = disown( cldh );
ASSERT( dummynrh == NRH_NULL );
adopt( parh, cldh, nrh );
cldh = nextcldh;
continue;
}
cldp = Node_map( cldh );
renamep = Node_map( renameh );
if ( renamep->n_nrh == NRH_NULL ) {
renamep->n_nrh = nrh;
} else {
namreg_del( nrh );
}
if ( renamep->n_parh == NH_NULL ) {
renamep->n_parh = parh;
}
cldp->n_flags |= NF_NEWORPH;
Node_unmap( renameh, &renamep );
Node_unmap( cldh, &cldp );
}
} else {
/* determine if we can unlink this node.
* if its not real, and not refed, simple.
* if real and not refed and there is at least
* one unreal refed node and no other real
* nodes around, must put this one in orphanage
* rather than unlinking it.
*/
bool_t mustorphpr;
bool_t canunlinkpr;
if ( inorphanagepr ) {
cldh = nextcldh;
continue;
}
mustorphpr = BOOL_FALSE;
canunlinkpr = ! isrefpr && ! isrealpr;
if ( ! isrefpr && isrealpr ) {
nh_t hardh;
bool_t neededpr;
hardh = link_hardh( ino, gen );
ASSERT( hardh != NH_NULL );
canunlinkpr = BOOL_FALSE;
neededpr = BOOL_FALSE;
/* tes@sgi.com:
* This loop seems to assume that
* REAL files come before NON-REALs
* so that we will break out first
* if we find a REAL file.
*/
while ( hardh != NH_NULL ) {
node_t *hardp;
bool_t hardisrefpr;
bool_t hardisrealpr;
nh_t nexthardh;
hardp = Node_map( hardh );
hardisrefpr = hardp->n_flags & NF_REFED;
hardisrealpr = hardp->n_flags & NF_REAL;
nexthardh = hardp->n_lnkh;
Node_unmap( hardh, &hardp );
if ( hardh != cldh && hardisrealpr ) {
break;
}
if ( hardisrefpr && ! hardisrealpr ) {
neededpr = BOOL_TRUE;
}
hardh = nexthardh;
}
if ( neededpr ) {
mustorphpr = BOOL_TRUE;
}
else {
canunlinkpr = BOOL_TRUE;
}
}
if ( mustorphpr ) {
/* rename file to orphanage */
nrh_t nrh;
ASSERT( ! canunlinkpr );
ok = Node2path( cldh,
path1,
_("tmp nondir rename src") );
if ( ! ok ) {
cldh = nextcldh;
continue;
}
nrh = disown( cldh );
ASSERT( nrh != NRH_NULL );
adopt( persp->p_orphh, cldh, NRH_NULL );
ok = Node2path( cldh,
path2,
_("tmp nondir rename dst") );
if ( ! ok ) {
/* REFERENCED */
nrh_t dummynrh;
dummynrh = disown( cldh );
ASSERT( dummynrh == NRH_NULL );
adopt( parh, cldh, nrh );
cldh = nextcldh;
continue;
}
mlog( MLOG_TRACE | MLOG_TREE,
"rename nondir %s to %s\n",
path1,
path2 );
rval = rename( path1, path2 );
if ( rval ) {
/* REFERENCED */
nrh_t dummynrh;
mlog( MLOG_NORMAL | MLOG_WARNING, _(
"unable to rename nondir %s "
"to nondir %s: %s\n"),
path1,
path2,
strerror( errno ));
dummynrh = disown( cldh );
ASSERT( dummynrh == NRH_NULL );
adopt( parh, cldh, nrh );
cldh = nextcldh;
continue;
}
namreg_del( nrh );
cldp = Node_map( cldh );
cldp->n_flags |= NF_NEWORPH;
Node_unmap( cldh, &cldp );
}
if ( canunlinkpr ) {
/* REFERENCED */
nrh_t nrh;
ASSERT( ! mustorphpr );
if ( isrealpr ) {
ok = Node2path( cldh, path1, _("rmdir") );
if ( ! ok ) {
cldh = nextcldh;
continue;
}
mlog( MLOG_TRACE | MLOG_TREE,
"unlink %s\n",
path1 );
rval = unlink( path1 );
if ( rval ) {
mlog( MLOG_NORMAL
|
MLOG_WARNING
|
MLOG_TREE, _(
"unable to unlink nondir "
"%s: %s\n"),
path1,
strerror( errno ));
cldh = nextcldh;
continue;
}
}
nrh = disown( cldh );
ASSERT( nrh != NRH_NULL );
link_out( cldh );
Node_free( &cldh );
}
}
/* next!
*/
cldh = nextcldh;
}
return BOOL_TRUE;
}
/* ARGSUSED */
static bool_t
mkdirs_recurse( nh_t parh, nh_t cldh, char *path )
{
while ( cldh != NH_NULL ) {
node_t *cldp;
bool_t isdirpr;
bool_t isselpr;
bool_t isrealpr;
bool_t isrefpr;
nh_t grandcldh;
nh_t nextcldh;
cldp = Node_map( cldh );
isdirpr = ( cldp->n_flags & NF_ISDIR );
isrealpr = ( cldp->n_flags & NF_REAL );
isrefpr = ( cldp->n_flags & NF_REFED );
isselpr = ( cldp->n_flags & NF_SUBTREE );
grandcldh = cldp->n_cldh;
nextcldh = cldp->n_sibh;
Node_unmap( cldh, &cldp );
/* if needed, create a directory and update real flag
*/
if ( isdirpr && ! isrealpr && isrefpr && isselpr ) {
intgen_t rval;
if ( ! Node2path( cldh, path, _("makedir") )) {
cldh = nextcldh;
continue;
}
if ( tranp->t_toconlypr ) {
rval = 0;
} else {
mlog( MLOG_TRACE | MLOG_TREE,
"mkdir %s\n",
path );
rval = mkdir( path, S_IRWXU );
}
if ( rval && errno != EEXIST ) {
mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_TREE, _(
"mkdir %s failed: %s\n"),
path,
strerror( errno ));
} else {
cldp = Node_map( cldh );
cldp->n_flags |= NF_REAL;
Node_unmap( cldh, &cldp );
isrealpr = BOOL_TRUE;
}
}
/* if a real selected directory, recurse
*/
if ( isdirpr && isrealpr && isselpr ) {
bool_t ok;
ok = mkdirs_recurse( cldh, grandcldh, path );
/* RECURSION */
if ( ! ok ) {
return BOOL_FALSE;
}
}
/* next!
*/
cldh = nextcldh;
}
return BOOL_TRUE;
}
static bool_t
rename_dirs( nh_t cldh,
char *path1,
char *path2 )
{
while ( cldh != NH_NULL ) {
node_t *cldp;
/* REFERENCED */
nh_t parh;
bool_t isdirpr;
nh_t renameh;
bool_t isrenamepr;
nh_t nextcldh;
nh_t newparh;
nh_t newnrh;
cldp = Node_map( cldh );
parh = cldp->n_parh;
isdirpr = cldp->n_flags & NF_ISDIR;
renameh = cldp->n_lnkh;
isrenamepr = isdirpr && renameh != NH_NULL;
nextcldh = cldp->n_sibh;
Node_unmap( cldh, &cldp );
ASSERT( parh == persp->p_orphh );
if ( isrenamepr ) {
node_t *renamep;
intgen_t rval;
/* REFERENCED */
nrh_t dummynrh;
bool_t ok;
renamep = Node_map( renameh );
newparh = renamep->n_parh;
newnrh = renamep->n_nrh;
Node_unmap( renameh, &renamep );
ok = Node2path( cldh, path1, _("rename dir") );
if ( ! ok ) {
cldh = nextcldh;
continue;
}
dummynrh = disown( cldh );
ASSERT( dummynrh == NRH_NULL );
adopt( newparh, cldh, newnrh );
ok = Node2path( cldh, path2, _("rename dir") );
if ( ! ok ) {
dummynrh = disown( cldh );
ASSERT( dummynrh == newnrh );
adopt( persp->p_orphh, cldh, NRH_NULL );
cldp = Node_map( cldh );
cldp->n_nrh = NRH_NULL;
Node_unmap( cldh, &cldp );
cldh = nextcldh;
continue;
}
mlog( MLOG_TRACE | MLOG_TREE,
"rename dir %s to %s\n",
path1,
path2 );
rval = rename( path1, path2 );
if ( rval ) {
mlog( MLOG_NORMAL | MLOG_WARNING, _(
"unable to rename dir %s "
"to dir %s: %s\n"),
path1,
path2,
strerror( errno ));
dummynrh = disown( cldh );
ASSERT( dummynrh == newnrh );
adopt( persp->p_orphh, cldh, NRH_NULL );
cldh = nextcldh;
continue;
}
cldp = Node_map( cldh );
cldp->n_flags &= ~NF_NEWORPH;
cldp->n_lnkh = NH_NULL;
Node_unmap( cldh, &cldp );
Node_free( &renameh );
}
/* next!
*/
cldh = nextcldh;
}
return BOOL_TRUE;
}
/* will call funcp for all links to be created. will abort if funcp
* ever returns FALSE;
*/
rv_t
tree_cb_links( xfs_ino_t ino,
gen_t gen,
int32_t ctime,
int32_t mtime,
bool_t ( * funcp )( void *contextp,
bool_t linkpr,
char *path1,
char *path2 ),
void *contextp,
char *path1,
char *path2 )
{
nh_t hardh;
nh_t nh;
char *path;
bool_t ok;
int rval;
if ( persp->p_truncategenpr ) {
gen = BIGGEN2GEN( gen );
}
/* find the hardhead
*/
hardh = link_hardh( ino, gen );
/* loop through all hard links, attempting to restore/link
*/
path = path1;
for ( nh = hardh ; nh != NH_NULL ; nh = link_nexth( nh )) {
node_t *np;
u_char_t flags;
char *reasonstr;
/* get the node flags
*/
np = Node_map( nh );
flags = np->n_flags;
Node_unmap( nh, &np );
/* build a pathname
*/
ok = Node2path( nh, path, _("restore") );
if ( ! ok ) {
continue;
}
/* skip if not in selected subtree
*/
if ( ! ( flags & NF_SUBTREE )) {
mlog( (MLOG_NITTY + 1) | MLOG_TREE,
"skipping %s (ino %llu gen %u): %s\n",
path,
ino,
gen,
"not in selected subtree" );
continue;
}
/* don't restore into the housekeeping directory
*/
if ( path_beginswith( path, tranp->t_hkdir )) {
mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_TREE, _(
"cannot restore %s (ino %llu gen %u): "
"pathname contains %s\n"),
path,
ino,
gen,
tranp->t_hkdir );
continue;
}
/* check if ok to overwrite: don't check if we've already
* been here and decided overwrite ok. if ok, set flag
* so we won't check again. in fact, can't check again
* since restore changes the answer.
*/
if ( ! ( flags & NF_WRITTEN )) {
bool_t exists;
if ( ! content_overwrite_ok( path,
ctime,
mtime,
&reasonstr,
&exists )) {
mlog( MLOG_TRACE | MLOG_TREE,
"skipping %s (ino %llu gen %u): %s\n",
path,
ino,
gen,
reasonstr );
continue;
} else {
np = Node_map( nh );
np->n_flags |= NF_WRITTEN;
Node_unmap( nh, &np );
/*
* We're the first stream to restore this file.
* Unlink it first to remove extended attributes
* that may have been set since the dump was
* taken.
*/
if ( ! tranp->t_toconlypr && exists ) {
rval = unlink( path );
if ( rval && errno != ENOENT ) {
mlog( MLOG_NORMAL |
MLOG_WARNING, _(
"unable to unlink "
"current file prior to "
"restore: "
"%s: %s: discarding\n"),
path,
strerror( errno ));
continue;
}
}
}
}
/* call callback to restore file / create hard link.
* returns !ok if should abort.
*/
if ( path == path2 ) {
mlog( MLOG_TRACE | MLOG_TREE,
"link %s to %s (%llu %u)\n",
path1,
path2,
ino,
gen );
} else {
mlog( MLOG_TRACE | MLOG_TREE,
"restoring %s (%llu %u)\n",
path1,
ino,
gen );
}
ok = ( * funcp )( contextp, path == path2, path1, path2 );
if ( ! ok ) {
return RV_NOTOK;
}
/* set flag, indicating node is now real
*/
np = Node_map( nh );
np->n_flags |= NF_REAL;
Node_unmap( nh, &np );
/* switch to second path buffer, for link paths
*/
path = path2;
}
/* if not yet peeled from tape, do so: place in orphanage if
* no references found (i.e., hard link list empty).
*/
if ( path == path1 ) {
if ( hardh != NH_NULL || persp->p_ignoreorphpr ) {
/*
* The file is referenced so the path is
* valid. This means that if path begins
* with 'orphanage' the file is one of two
* things:
* 1) It's a file that really is an
* orphanage file from a previous restore
* that has now ended up on this dump tape.
* We don't really want to restore this file
* but, it's harmless to do so, it should
* happen rarely, and the path name is
* indistinguishable from ...
* 2) A file whose name was never resolved
* from root because of file corruption.
* Some granparent dir (parent dir of it's
* parent dir) was corrupted so the code that
* walks the trees was thus never able to set
* the NF_SUBTREE flag. It then ends up here
* with a non-resolved name but a valid
* hard reference. We really need to give
* the admin the opportunity to get this
* data since one corrupted dirnode would
* make the whole tree of data
* unreachable. pv698761
*/
if ( persp->p_ignoreorphpr || (strncmp(ORPH_NAME, path,
strlen(ORPH_NAME)) != 0)) {
mlog( MLOG_DEBUG | MLOG_TREE,
"discarding %llu %u\n",
ino,
gen );
ok = ( * funcp )( contextp, BOOL_FALSE, 0, 0 );
if ( ! ok ) {
return RV_NOTOK;
}
} else {
if ( ! tranp->t_toconlypr ) {
char *dir;
char tmp[PATH_MAX];
strcpy(tmp, path);
dir = dirname(tmp);
mkdir_r(dir);
}
mlog (MLOG_VERBOSE | MLOG_NOTE | MLOG_TREE, _(
"ino %llu salvaging file,"
" placing in %s\n"), ino, path1);
ok = ( * funcp )( contextp, path == path2,
path1, path2 );
if ( ! ok ) {
return RV_NOTOK;
}
}
} else {
mlog( MLOG_VERBOSE | MLOG_NOTE | MLOG_TREE, _(
"ino %llu gen %u not referenced: "
"placing in orphanage\n"),
ino,
gen );
nh = Node_alloc( ino,
gen,
NRH_NULL,
DAH_NULL,
NF_REAL | NF_NEWORPH );
if (nh == NH_NULL) {
mlog( MLOG_ERROR | MLOG_TREE, _(
"node allocation failed when placing ino %llu"
" in orphanage\n"), ino);
return RV_ERROR; /* allocation failed */
}
link_in( nh );
adopt( persp->p_orphh, nh, NRH_NULL );
ok = Node2path( nh, path1, _("orphan") );
ASSERT( ok );
( void )( * funcp )( contextp, BOOL_FALSE, path1,path2);
}
}
return RV_OK;
}
/* uses flags cleared during directory restore (NF_DUMPEDDIR and NF_REFED )
* to determine what directory entries are no longer needed. this can
* be done because whenever a directory chenges, it and all of its current
* entries are dumped. so if an entry is dumped which is a dir, but that
* dir is not dumped, all of that directories entries are needed and so must
* have their REFED flag cleared.
*
* does a recursive depth-wise descent of the tree, following this rule to
* clear the REFED flags.
*/
static void tree_adjref_recurse( nh_t cldh,
bool_t pardumpedpr,
bool_t parrefedpr );
bool_t
tree_adjref( void )
{
tree_adjref_recurse( persp->p_rooth, BOOL_FALSE, BOOL_TRUE );
return BOOL_TRUE;
}
static void
tree_adjref_recurse( nh_t cldh,
bool_t pardumpedpr,
bool_t parrefedpr )
{
nh_t grandcldh;
bool_t clddumpedpr;
bool_t cldrefedpr;
{
node_t *cldp;
cldp = Node_map( cldh );
if ( ! pardumpedpr && parrefedpr ) {
cldp->n_flags |= NF_REFED;
}
clddumpedpr = ( intgen_t )cldp->n_flags & NF_DUMPEDDIR;
cldrefedpr = ( intgen_t )cldp->n_flags & NF_REFED;
grandcldh = cldp->n_cldh;
Node_unmap( cldh, &cldp );
}
while ( grandcldh != NH_NULL ) {
node_t *grandcldp;
nh_t nextgrandcldh;
tree_adjref_recurse( grandcldh, clddumpedpr, cldrefedpr );
/* RECURSION */
grandcldp = Node_map( grandcldh );
nextgrandcldh = grandcldp->n_sibh;
Node_unmap( grandcldh, &grandcldp );
grandcldh = nextgrandcldh;
}
}
static bool_t tree_extattr_recurse( nh_t parh,
nh_t cldh,
bool_t ( * cbfunc )( char *path,
dah_t dah ),
char *path );
bool_t
tree_extattr( bool_t ( * cbfunc )( char *path, dah_t dah ), char *path )
{
node_t *rootp;
nh_t cldh;
bool_t ok;
rootp = Node_map( persp->p_rooth );
cldh = rootp->n_cldh;
Node_unmap( persp->p_rooth, &rootp );
ok = tree_extattr_recurse( persp->p_rooth, cldh, cbfunc, path );
return ok;
}
static bool_t
tree_extattr_recurse( nh_t parh,
nh_t cldh,
bool_t ( * cbfunc )( char *path, dah_t dah ),
char *path )
{
node_t *parp;
dah_t dah;
bool_t ok;
/* first update all children
*/
while ( cldh != NH_NULL ) {
node_t *cldp;
bool_t isdirpr;
bool_t isselpr;
bool_t isrealpr;
nh_t grandcldh;
nh_t nextcldh;
cldp = Node_map( cldh );
isdirpr = ( cldp->n_flags & NF_ISDIR );
isrealpr = ( cldp->n_flags & NF_REAL );
isselpr = ( cldp->n_flags & NF_SUBTREE );
grandcldh = cldp->n_cldh;
nextcldh = cldp->n_sibh;
Node_unmap( cldh, &cldp );
/* if a real selected directory, recurse
*/
if ( isdirpr && isrealpr && isselpr ) {
bool_t ok;
ok = tree_extattr_recurse( cldh,
grandcldh,
cbfunc,
path );
/* RECURSION */
if ( ! ok ) {
return BOOL_FALSE;
}
}
/* next!
*/
cldh = nextcldh;
}
/* now update self
*/
parp = Node_map( parh );
dah = parp->n_dah;
Node_unmap( parh, &parp );
if ( ! Node2path( parh, path, _("set dir extattr") )) {
mlog (MLOG_NORMAL | MLOG_WARNING | MLOG_TREE, _(
"tree_extattr_recurse: Could not convert node to "
"path for %s\n"), path);
return BOOL_TRUE;
}
if ( dah != DAH_NULL ) {
ok = ( * cbfunc )( path, dah );
} else {
ok = BOOL_TRUE;
}
return ok;
}
struct phcb {
char *path1;
char *path2;
bool_t ok;
};
typedef struct phcb phcb_t;
static bool_t proc_hardlinks_cb( void *contextp, nh_t hardheadh );
static bool_t
proc_hardlinks( char *path1, char *path2 )
{
phcb_t phcb;
/* have callback invoked for all hardheads
*/
phcb.path1 = path1;
phcb.path2 = path2;
phcb.ok = BOOL_TRUE;
link_headiter( proc_hardlinks_cb, ( void * )&phcb );
return phcb.ok;
}
/* called for every hard head
*
* tes@sgi.com:
* This code processes the hardlinks, extracting out a
* src_list - real & !ref
* dest_list - !real & ref
* The src_list are the entries to delete and the dst_list
* are the new_entries to create.
* 1. If we have a src and a dst then we can do a rename.
* 2. If we have extra src nodes then we can unlink them.
* 3. If we have extra dst nodes then we can link them.
* HOWEVER, the linking in of the new nodes is actually handled
* in the restore_file_cb() code which on restoral creates
* the file and all its hardlinks.
* ALSO, I can not see how the src_list can have more than
* 1 node in it, since in noref_elim_recurse() it unlinks all
* extra deleted entries (real & !ref).
* Thus, steps 2 and 3 are handled in noref_elim_recurse().
*/
static bool_t
proc_hardlinks_cb( void *contextp, nh_t hardheadh )
{
phcb_t *phcbp = ( phcb_t * )contextp;
node_t *hardheadp;
xfs_ino_t ino;
gen_t gen;
bool_t isdirpr;
nh_t rnsrcheadh;
nh_t rndstheadh;
nh_t lnsrch;
nh_t nh;
link_iter_context_t link_iter_context;
bool_t ok;
intgen_t rval;
/* skip directories
*/
hardheadp = Node_map( hardheadh );
ino = hardheadp->n_ino;
gen = hardheadp->n_gen;
isdirpr = hardheadp->n_flags & NF_ISDIR;
Node_unmap( hardheadh, &hardheadp );
if ( isdirpr ) {
return BOOL_TRUE;
}
mlog( MLOG_DEBUG | MLOG_TREE,
"processing hardlinks to %llu %u\n",
ino,
gen );
/* first pass through hard link list: for each node, leave on
* list, unlink and place on rename src list, unlink and place on
* rename dst list, or unlink and discard. note a node available
* to link from, in case we need it.
*/
rnsrcheadh = NH_NULL;
rndstheadh = NH_NULL;
lnsrch = NH_NULL;
link_iter_init( &link_iter_context, hardheadh );
while ( ( nh = link_iter_next( &link_iter_context )) != NH_NULL ) {
node_t *np = Node_map( nh );
bool_t isrealpr = np->n_flags & NF_REAL;
bool_t isrefpr = np->n_flags & NF_REFED;
bool_t isselpr = np->n_flags & NF_SUBTREE;
/* if unrefed, unreal, free node etc. (sel doesn't matter)
*/
if ( ! isrealpr && ! isrefpr ) {
mlog( MLOG_NITTY | MLOG_TREE,
"freeing node %x: not real, not referenced\n",
nh );
link_iter_unlink( &link_iter_context, nh );
Node_unmap( nh, &np );
( void )disown( nh );
Node_free( &nh );
continue;
}
/* not real, refed, but not selected, can't help
*/
if ( ! isrealpr && isrefpr && ! isselpr ) {
mlog( MLOG_NITTY | MLOG_TREE,
"skipping node %x: not selected\n",
nh );
Node_unmap( nh, &np );
continue;
}
/* if unreal, refed, sel, add to dst list,
*/
if ( ! isrealpr && isrefpr && isselpr ) {
mlog( MLOG_NITTY | MLOG_TREE,
"making node %x dst: "
"not real, refed, sel\n",
nh );
link_iter_unlink( &link_iter_context, nh );
np->n_lnkh = rndstheadh;
rndstheadh = nh;
Node_unmap( nh, &np );
continue;
}
/* if real, unrefed, sel, add to src list
*/
if ( isrealpr && ! isrefpr && isselpr ) {
mlog( MLOG_NITTY | MLOG_TREE,
"making node %x src: real, not refed, sel\n",
nh );
link_iter_unlink( &link_iter_context, nh );
np->n_lnkh = rnsrcheadh;
rnsrcheadh = nh;
Node_unmap( nh, &np );
continue;
}
/* would like to get rid of but not selected, or
* real and referenced, leave alone (sel doesn't matter).
* consider as a lnk src, since real and not going away.
*/
if ( isrealpr && ( isrefpr || !isselpr ) ) {
mlog( MLOG_NITTY | MLOG_TREE,
"skipping node %x: %s\n",
nh,
isselpr ? "real and ref" : "real and not sel" );
Node_unmap( nh, &np );
if ( lnsrch == NH_NULL ) {
mlog( MLOG_NITTY | MLOG_TREE,
"node %x will be link src\n",
nh );
lnsrch = nh;
}
continue;
}
ASSERT( 0 );
}
/* now pass through dst list, doing renames if src list not empty,
* otherwise links if a lnk src available, otherwise put back in link
* list
*/
while ( rndstheadh != NH_NULL ) {
nh_t dsth;
node_t *dstp;
bool_t successpr;
dsth = rndstheadh;
dstp = Node_map( dsth );
rndstheadh = dstp->n_lnkh;
dstp->n_lnkh = NH_NULL;
Node_unmap( dsth, &dstp );
/* build pathname to dst
*/
ok = Node2path( dsth, phcbp->path2, _("rename to") );
if ( ! ok ) {
link_in( dsth );
continue;
}
successpr = BOOL_FALSE;
while ( ! successpr && rnsrcheadh != NH_NULL ) {
nh_t srch;
nrh_t nrh;
node_t *srcp;
srch = rnsrcheadh;
srcp = Node_map( srch );
rnsrcheadh = srcp->n_lnkh;
srcp->n_lnkh = NH_NULL;
Node_unmap( srch, &srcp );
/* build a path to src
*/
ok = Node2path( srch, phcbp->path1, _("rename from") );
if ( ! ok ) {
link_in( srch );
continue;
}
if ( tranp->t_toconlypr ) {
rval = 0;
} else {
mlog( MLOG_TRACE | MLOG_TREE,
"rename nondir %s to %s\n",
phcbp->path1,
phcbp->path2 );
rval = rename( phcbp->path1,
phcbp->path2 );
}
if ( rval ) {
mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_TREE, _(
"unable to rename nondir "
"%s to %s: %s\n"),
phcbp->path1,
phcbp->path2,
strerror( errno ));
link_in( srch );
continue;
}
nrh = disown( srch );
if ( nrh != NRH_NULL ) {
namreg_del( nrh );
}
Node_free( &srch );
successpr = BOOL_TRUE;
}
/* tes@sgi.com: note: loop of one iteration only
*/
while ( ! successpr && lnsrch != NH_NULL ) {
ok = Node2path( lnsrch, phcbp->path1, _("link") );
if ( ! ok ) {
mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_TREE, _(
"unable to use %s "
"as a hard link source\n"),
phcbp->path1 );
lnsrch = NH_NULL;
continue;
}
if ( tranp->t_toconlypr ) {
rval = 0;
} else {
mlog( MLOG_TRACE | MLOG_TREE,
"link nondir %s to %s\n",
phcbp->path1,
phcbp->path2 );
rval = link( phcbp->path1,
phcbp->path2 );
}
if ( rval ) {
mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_TREE,
"unable to link nondir "
"%s to %s: %s\n",
phcbp->path1,
phcbp->path2,
strerror( errno ));
break;
}
successpr = BOOL_TRUE;
}
if ( ! successpr ) {
mlog( MLOG_NITTY | MLOG_TREE,
"no link src for node %x\n",
dsth );
} else {
dstp = Node_map( dsth );
dstp->n_flags |= NF_REAL;
Node_unmap( dsth, &dstp );
}
link_in( dsth );
}
/* finally, pass through remaining src list, unlinking/disowning.
* tes@sgi.com: don't believe this will happen as this step
* should now be done in noref_elim_recurse().
*/
while ( rnsrcheadh != NH_NULL ) {
nh_t srch;
node_t *srcp;
bool_t ok;
srch = rnsrcheadh;
srcp = Node_map( srch );
rnsrcheadh = srcp->n_lnkh;
srcp->n_lnkh = NH_NULL;
Node_unmap( srch, &srcp );
ok = Node2path( srch, phcbp->path1, _("unlink") );
if ( ! ok ) {
link_in( srch );
continue;
}
if ( tranp->t_toconlypr ) {
rval = 0;
} else {
mlog( MLOG_TRACE | MLOG_TREE,
"unlink nondir %s\n",
phcbp->path1 );
rval = unlink( phcbp->path1 );
}
if ( rval ) {
mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_TREE, _(
"unable to unlink %s: %s\n"),
phcbp->path1,
strerror( errno ));
link_in( srch );
} else {
nrh_t nrh;
nrh = disown( srch );
if ( nrh != NRH_NULL ) {
namreg_del( nrh );
}
Node_free( &srch );
}
}
return BOOL_TRUE;
}
/* traverse tree depth-wise bottom-up for dirs no longer referenced.
* if non-empty, move children to orphanage
*/
bool_t
tree_setattr( char *path )
{
bool_t ok;
node_t *rootp;
ok = tree_setattr_recurse( persp->p_rooth, path );
if ( restore_rootdir_permissions && ok ) {
rootp = Node_map( persp->p_rooth );
/* "." is cwd which is the destination dir */
setdirattr( rootp->n_dah, "." );
Node_unmap( persp->p_rooth, &rootp );
}
return ok;
}
static bool_t
tree_setattr_recurse( nh_t parh, char *path )
{
node_t *parp = Node_map( parh );
nh_t cldh = parp->n_cldh;
Node_unmap( parh, &parp );
while ( cldh != NH_NULL ) {
nh_t nextcldh;
/* get the node attributes
*/
node_t *cldp = Node_map( cldh );
bool_t isdirpr = ( cldp->n_flags & NF_ISDIR );
bool_t isselpr = ( cldp->n_flags & NF_SUBTREE );
bool_t isrealpr = ( cldp->n_flags & NF_REAL );
dah_t dah = cldp->n_dah;
/* get next cld
*/
nextcldh = cldp->n_sibh;
Node_unmap( cldh, &cldp );
/* if is a real selected dir, go ahead.
*/
if ( isdirpr && isselpr && isrealpr ) {
bool_t ok;
ok = tree_setattr_recurse( cldh, path ); /* RECURSION */
if ( ! ok ) {
Node_unmap( cldh, &cldp );
return BOOL_FALSE;
}
if ( dah != DAH_NULL ) {
bool_t ok;
ok = Node2path( cldh, path, _("set dirattr") );
if ( ok ) {
setdirattr( dah, path );
}
}
}
cldh = nextcldh;
}
return BOOL_TRUE;
}
static void
setdirattr( dah_t dah, char *path )
{
mode_t mode;
struct utimbuf utimbuf;
struct fsxattr fsxattr;
intgen_t rval;
size_t hlen;
void *hanp;
intgen_t fd = -1;
if ( dah == DAH_NULL )
return;
if ( tranp->t_dstdirisxfspr ) {
if (path_to_handle(path, &hanp, &hlen)) {
mlog( MLOG_NORMAL | MLOG_WARNING,
_("path_to_handle of %s failed:%s\n"),
path, strerror( errno ));
} else {
fd = open_by_handle(hanp, hlen, O_RDONLY);
if (fd < 0) {
mlog( MLOG_NORMAL | MLOG_WARNING,
_("open_by_handle of %s failed:%s\n"),
path, strerror( errno ));
}
free_handle(hanp, hlen);
}
}
if ( tranp->t_dstdirisxfspr && persp->p_restoredmpr ) {
fsdmidata_t fssetdm;
fssetdm.fsd_dmevmask = dirattr_get_dmevmask( dah );
fssetdm.fsd_padding = 0; /* not used */
fssetdm.fsd_dmstate = ( u_int16_t )dirattr_get_dmstate( dah );
/* restore DMAPI event settings etc.
*/
rval = ioctl( fd,
XFS_IOC_FSSETDM,
( void * )&fssetdm );
if ( rval ) {
mlog( errno == EINVAL
?
( MLOG_NITTY + 1 ) | MLOG_TREE
:
MLOG_NITTY | MLOG_TREE,
"set DMI attributes"
" of %s failed: %s\n",
path,
strerror( errno ));
}
}
utimbuf.actime = dirattr_get_atime( dah );
utimbuf.modtime = dirattr_get_mtime( dah );
rval = utime( path, &utimbuf );
if ( rval ) {
mlog( MLOG_VERBOSE | MLOG_TREE, _(
"could not set access and modification times"
" of %s: %s\n"),
path,
strerror( errno ));
}
mode = dirattr_get_mode( dah );
if ( persp->p_ownerpr ) {
rval = chown( path,
dirattr_get_uid( dah ),
dirattr_get_gid( dah ));
if ( rval ) {
mlog( MLOG_NORMAL | MLOG_TREE, _(
"chown (uid=%d, gid=%d) %s failed: %s\n"),
dirattr_get_uid( dah ),
dirattr_get_gid( dah ),
path,
strerror( errno ));
}
}
rval = chmod( path, mode );
if ( rval ) {
mlog( MLOG_NORMAL | MLOG_TREE, _(
"chmod %s failed: %s\n"),
path,
strerror( errno ));
}
/* set the extended inode flags
*/
if ( !tranp->t_dstdirisxfspr )
return;
memset((void *)&fsxattr, 0, sizeof( fsxattr ));
fsxattr.fsx_xflags = dirattr_get_xflags( dah );
fsxattr.fsx_extsize = dirattr_get_extsize( dah );
fsxattr.fsx_projid = dirattr_get_projid( dah );
rval = ioctl( fd,
XFS_IOC_FSSETXATTR,
(void *)&fsxattr);
if ( rval < 0 ) {
mlog(MLOG_NORMAL | MLOG_WARNING,
_("attempt to set "
"extended attributes "
"(xflags 0x%x, "
"extsize = 0x%x, "
"projid = 0x%x)"
"of %s failed: "
"%s\n"),
fsxattr.fsx_xflags,
fsxattr.fsx_extsize,
fsxattr.fsx_projid,
path,
strerror(errno));
}
( void )close( fd );
}
/* deletes orphanage if empty, else warns
*/
bool_t
tree_delorph( void )
{
intgen_t rval;
rval = rmdir( tranp->t_orphdir );
if ( rval ) {
if ( errno == EEXIST ) {
mlog(MLOG_NORMAL | MLOG_WARNING | MLOG_TREE, _(
"unable to rmdir %s: not empty\n"),
tranp->t_orphdir );
} else {
mlog(MLOG_NORMAL | MLOG_WARNING | MLOG_TREE, _(
"unable to rmdir %s: %s\n"),
tranp->t_orphdir,
strerror( errno ));
}
}
return BOOL_TRUE;
}
/* definition of locally defined static functions ****************************/
/* interactive subtree abstraction *******************************************/
static void tsi_cmd_inst( void *, dlog_pcbp_t, void * );
static void tsi_cmd_pwd( void *, dlog_pcbp_t, void * );
static void tsi_cmd_ls( void *, dlog_pcbp_t, void * );
static void tsi_cmd_cd( void *, dlog_pcbp_t, void * );
static void tsi_cmd_add( void *, dlog_pcbp_t, void * );
static void tsi_cmd_delete( void *, dlog_pcbp_t, void * );
static void tsi_cmd_extract( void *, dlog_pcbp_t, void * );
static void tsi_cmd_quit( void *, dlog_pcbp_t, void * );
static void tsi_cmd_help( void *, dlog_pcbp_t, void * );
static void tsi_cmd_parse( char * );
static dlog_ucbp_t tsi_cmd_match( void );
#define PREAMBLEMAX 3
#define ACKMAX 3
#define POSTAMBLEMAX 3
bool_t
tree_subtree_inter( void )
{
fold_t fold;
char *preamblestr[ PREAMBLEMAX ];
size_t preamblecnt;
char *ackstr[ ACKMAX ];
size_t ackcnt;
char *postamblestr[ POSTAMBLEMAX ];
size_t postamblecnt;
dlog_ucbp_t cmdp;
restart:
/* make the current working directory the root of the fs
*/
tranp->t_inter.i_cwdh = persp->p_rooth;
/* begin the dialog
*/
preamblecnt = 0;
fold_init( fold, _("subtree selection dialog"), '=' );
preamblestr[ preamblecnt++ ] = "\n";
preamblestr[ preamblecnt++ ] = fold;
preamblestr[ preamblecnt++ ] = "\n\n";
ASSERT( preamblecnt <= PREAMBLEMAX );
dlog_begin( preamblestr, preamblecnt );
/* execute commands until time to extract or quit. always begin with
* an implicit instructions command. if see SIGINT, give main thread
* dialog a chance to decide if a session interrupt is wanted.
*/
cmdp = tsi_cmd_inst;
do {
char buf[ MAXPATHLEN ];
const ix_t abortix = 1;
const ix_t sigintix = 2;
const ix_t okix = 3;
ix_t responseix;
/* execute command and get response
*/
responseix = dlog_string_query( cmdp,
0,
buf,
sizeof( buf ),
0,
IXMAX, /* timeout ix */
sigintix, /* sigint ix */
abortix, /* sighup ix */
abortix, /* sigquit ix */
okix ); /* ok ix */
/* ack the response
*/
ackcnt = 0;
if ( responseix == sigintix ) {
ackstr[ ackcnt++ ] = _("keyboard interrupt\n");
} else if ( responseix == abortix ) {
ackstr[ ackcnt++ ] = _("abort\n");
} else {
ASSERT( responseix == okix );
}
ASSERT( ackcnt <= ACKMAX );
dlog_string_ack( ackstr,
ackcnt );
/* exception handling
*/
if ( responseix != okix ) {
/* if exception, end the dialog. may restart
* if operator decidesd not to intr.
*/
postamblecnt = 0;
fold_init( fold, _("end dialog"), '-' );
postamblestr[ postamblecnt++ ] = "\n";
postamblestr[ postamblecnt++ ] = fold;
postamblestr[ postamblecnt++ ] = "\n\n";
ASSERT( postamblecnt <= POSTAMBLEMAX );
dlog_end( postamblestr, postamblecnt );
/* if sighup or sigquit, immediately quit
*/
if ( responseix == abortix ) {
return BOOL_FALSE;
}
/* if sigint, allow main thread to decide if
* operator really wants to quit
*/
ASSERT( responseix == sigintix );
if ( cldmgr_stop_requested( )) {
return BOOL_FALSE;
}
sleep( 1 ); /* to allow main thread to begin dialog */
mlog( MLOG_NORMAL | MLOG_BARE | MLOG_TREE,
"" ); /* block until main thread dialog done */
sleep( 1 ); /* let main thread ask children to die */
if ( cldmgr_stop_requested( )) {
return BOOL_FALSE;
}
mlog( MLOG_DEBUG | MLOG_TREE,
"retrying interactive subtree selection dialog\n" );
goto restart;
}
tsi_cmd_parse( buf );
cmdp = tsi_cmd_match( );
if ( ! cmdp ) {
cmdp = tsi_cmd_help;
}
} while ( cmdp != tsi_cmd_quit && cmdp != tsi_cmd_extract );
postamblecnt = 0;
fold_init( fold, _("end dialog"), '-' );
postamblestr[ postamblecnt++ ] = "\n";
postamblestr[ postamblecnt++ ] = fold;
postamblestr[ postamblecnt++ ] = "\n\n";
ASSERT( postamblecnt <= POSTAMBLEMAX );
dlog_end( postamblestr, postamblecnt );
/* pv 773569 - quit is not a reason to consider session
* to be interrupted (we haven't started yet) so just unmark
* any selected directories and return */
if ( cmdp == tsi_cmd_quit ) {
mlog( MLOG_NORMAL, _("Unmark and quit\n") );
selsubtree( persp->p_rooth , BOOL_FALSE );
}
return BOOL_TRUE;
}
static void
tsi_cmd_inst( void *ctxp,
dlog_pcbp_t pcb,
void *pctxp )
{
tsi_cmd_help( ctxp, pcb, pctxp );
}
static void
tsi_cmd_pwd( void *ctxp,
dlog_pcbp_t pcb,
void *pctxp )
{
/* special case root
*/
if ( tranp->t_inter.i_cwdh == persp->p_rooth ) {
( * pcb )( pctxp, "cwd is fs root\n" );
return;
}
/* ascend tree recursively, print path on way back
*/
tsi_cmd_pwd_recurse( ctxp, pcb, pctxp, tranp->t_inter.i_cwdh );
( * pcb )( pctxp, "\n" );
}
static void
tsi_cmd_pwd_recurse( void *ctxp,
dlog_pcbp_t pcb,
void *pctxp,
nh_t nh )
{
node_t *np;
register nh_t parh;
/* REFERENCED */
register intgen_t namelen;
nrh_t nrh;
ASSERT( nh != NH_NULL );
np = Node_map( nh );
nrh = np->n_nrh;
parh = np->n_parh;
Node_unmap( nh, &np );
if ( parh != persp->p_rooth ) {
tsi_cmd_pwd_recurse( ctxp, pcb, pctxp, parh );
/* RECURSION */
( * pcb )( pctxp, "/" );
}
ASSERT( nrh != NRH_NULL );
namelen = namreg_get( nrh,
tranp->t_inter.i_name,
sizeof( tranp->t_inter.i_name ));
ASSERT( namelen > 0 );
( * pcb )( pctxp, tranp->t_inter.i_name );
}
/* ARGSUSED */
static void
tsi_cmd_ls( void *ctxp,
dlog_pcbp_t pcb,
void *pctxp )
{
size_t argc = tranp->t_inter.i_argc;
char *arg = ( argc > 1 ) ? tranp->t_inter.i_argv[ 1 ] : 0;
bool_t ok;
nh_t cldh;
nh_t parh;
nh_t namedh;
xfs_ino_t ino;