4701 lines
102 KiB
C
4701 lines
102 KiB
C
#ident "$Header: /proj/irix6.5.7m/isms/eoe/cmd/xfs/dump/restore/RCS/tree.c,v 1.20 1999/08/03 15:53:30 cwf Exp $"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/utime.h>
|
|
#include <sys/uuid.h>
|
|
#include <limits.h>
|
|
#include <time.h>
|
|
#include <sys/fs/xfs_itable.h>
|
|
#include <sys/handle.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include "types.h"
|
|
#include "jdm.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 "win.h"
|
|
#include "node.h"
|
|
#include "tree.h"
|
|
#include "libgen.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 eigth, rest goes to node array
|
|
*/
|
|
#define HASHSZ_PERVM 8
|
|
|
|
|
|
/* reserve the first page for persistent state
|
|
*/
|
|
struct pers {
|
|
ino64_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
|
|
*/
|
|
};
|
|
|
|
typedef struct pers pers_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[ MAXNAMELEN ];
|
|
};
|
|
|
|
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
|
|
*/
|
|
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[ MAXNAMELEN ];
|
|
/* 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 40
|
|
|
|
struct node {
|
|
ino64_t n_ino; /* 8 8 ino */
|
|
nrh_t n_nrh; /* 4 12 handle to name in name registry */
|
|
dah_t n_dah; /* 4 16 handle to directory attributes */
|
|
nh_t n_hashh; /* 4 20 hash array */
|
|
nh_t n_parh; /* 4 24 parent */
|
|
nh_t n_sibh; /* 4 28 sibling list */
|
|
nh_t n_cldh; /* 4 32 children list */
|
|
nh_t n_lnkh; /* 4 36 hard link list */
|
|
gen_t n_gen; /* 2 38 generation count mod 0x10000 */
|
|
u_char_t n_flags; /* 1 39 action and state flags */
|
|
u_char_t n_nodehkbyte; /* 1 40 given to node abstraction */
|
|
};
|
|
|
|
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;
|
|
|
|
|
|
/* declarations of externally defined global symbols *************************/
|
|
|
|
extern void usage( void );
|
|
extern size_t pgsz;
|
|
extern size_t pgmask;
|
|
|
|
|
|
/* forward declarations of locally defined static functions ******************/
|
|
|
|
static nh_t Node_alloc( ino64_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 );
|
|
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( ino64_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 void hash_in( nh_t nh );
|
|
static void hash_out( nh_t nh );
|
|
static nh_t hash_find( ino64_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,
|
|
ino64_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 pers_t *persp = 0;
|
|
static tran_t *tranp = 0;
|
|
static char *persname = PERS_NAME;
|
|
static char *orphname = ORPH_NAME;
|
|
static ino64_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,
|
|
ino64_t rootino,
|
|
ino64_t firstino,
|
|
ino64_t lastino,
|
|
size64_t dircnt,
|
|
size64_t nondircnt,
|
|
size64_t vmsz,
|
|
bool_t fullpr,
|
|
bool_t restoredmpr )
|
|
{
|
|
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;
|
|
|
|
/* 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,
|
|
strerror( errno ));
|
|
} 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 = ( pers_t * ) mmap64( 0,
|
|
PERSSZ,
|
|
PROT_READ | PROT_WRITE,
|
|
MAP_SHARED | MAP_AUTOGROW,
|
|
tranp->t_persfd,
|
|
( off64_t )0 );
|
|
if ( persp == ( pers_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.
|
|
*/
|
|
persp->p_rootino = rootino;
|
|
persp->p_rooth = Node_alloc( rootino,
|
|
0, /* gen cnt */
|
|
NRH_NULL,
|
|
DAH_NULL,
|
|
NF_ISDIR | NF_REAL );
|
|
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 );
|
|
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;
|
|
|
|
return BOOL_TRUE;
|
|
}
|
|
|
|
bool_t
|
|
tree_sync( char *hkdir, char *dstdir, bool_t toconlypr, bool_t fullpr )
|
|
{
|
|
off64_t nodeoff;
|
|
char *perspath;
|
|
bool_t ok;
|
|
intgen_t rval;
|
|
|
|
#ifdef SESSCPLT
|
|
if ( persp ) {
|
|
return BOOL_TRUE;
|
|
}
|
|
#endif /* SESSCPLT */
|
|
|
|
/* 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;
|
|
|
|
/* 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 = ( pers_t * ) mmap64( 0,
|
|
PERSSZ,
|
|
PROT_READ | PROT_WRITE,
|
|
MAP_SHARED | MAP_AUTOGROW,
|
|
tranp->t_persfd,
|
|
( off64_t )0 );
|
|
if ( persp == ( pers_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;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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;
|
|
ino64_t ino = fhdrp->fh_stat.bs_ino;
|
|
u_int32_t biggen = fhdrp->fh_stat.bs_gen;
|
|
gen_t gen = BIGGEN2GEN( biggen );
|
|
dah_t dah;
|
|
|
|
/* 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,
|
|
biggen );
|
|
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,
|
|
biggen );
|
|
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,
|
|
biggen );
|
|
}
|
|
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,
|
|
biggen );
|
|
if ( ! tranp->t_toconlypr ) {
|
|
dah = dirattr_add( fhdrp );
|
|
} else {
|
|
dah = DAH_NULL;
|
|
}
|
|
hardh = Node_alloc( ino,
|
|
gen,
|
|
NRH_NULL,
|
|
dah,
|
|
NF_ISDIR | NF_NEWORPH );
|
|
link_in( hardh );
|
|
adopt( persp->p_orphh, hardh, NRH_NULL );
|
|
*dahp = dah;
|
|
}
|
|
|
|
return hardh;
|
|
}
|
|
|
|
void
|
|
tree_addent( nh_t parh, ino64_t ino, size_t g, char *name, size_t namelen )
|
|
{
|
|
gen_t gen = BIGGEN2GEN( g );
|
|
nh_t hardh;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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 ) {
|
|
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 );
|
|
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 );
|
|
hardp->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 );
|
|
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 );
|
|
link_in( hardh );
|
|
adopt( parh, hardh, nrh );
|
|
mlog( MLOG_DEBUG | MLOG_TREE,
|
|
"dirent %s %llu %u: "
|
|
"adding (new)\n",
|
|
name,
|
|
ino,
|
|
gen );
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
ino64_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;
|
|
ino64_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;
|
|
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;
|
|
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 ) {
|
|
canunlinkpr = BOOL_TRUE;
|
|
break;
|
|
}
|
|
if ( hardisrefpr && ! hardisrealpr ) {
|
|
neededpr = BOOL_TRUE;
|
|
}
|
|
hardh = nexthardh;
|
|
}
|
|
if ( neededpr ) {
|
|
mustorphpr = BOOL_TRUE;
|
|
}
|
|
}
|
|
|
|
if ( mustorphpr ) {
|
|
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;
|
|
*/
|
|
void
|
|
tree_cb_links( ino64_t ino,
|
|
u_int32_t biggen,
|
|
int32_t ctime,
|
|
int32_t mtime,
|
|
bool_t ( * funcp )( void *contextp,
|
|
bool_t linkpr,
|
|
char *path1,
|
|
char *path2 ),
|
|
void *contextp,
|
|
char *path1,
|
|
char *path2 )
|
|
{
|
|
gen_t gen = BIGGEN2GEN( biggen );
|
|
nh_t hardh;
|
|
nh_t nh;
|
|
char *path;
|
|
bool_t ok;
|
|
int rval;
|
|
|
|
/* 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 )) {
|
|
if ( ! content_overwrite_ok( path,
|
|
ctime,
|
|
mtime,
|
|
&reasonstr )) {
|
|
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 ) {
|
|
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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
} else {
|
|
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;
|
|
}
|
|
}
|
|
} else {
|
|
mlog( MLOG_VERBOSE | MLOG_NOTE | MLOG_TREE,
|
|
"ino %llu gen %u not referenced: "
|
|
"placing in orphanage\n",
|
|
ino,
|
|
biggen );
|
|
nh = Node_alloc( ino,
|
|
gen,
|
|
NRH_NULL,
|
|
DAH_NULL,
|
|
NF_REAL | NF_NEWORPH );
|
|
link_in( nh );
|
|
adopt( persp->p_orphh, nh, NRH_NULL );
|
|
ok = Node2path( nh, path1, "orphan" );
|
|
assert( ok );
|
|
( void )( * funcp )( contextp, BOOL_FALSE, path1,path2);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
#ifdef EXTATTR
|
|
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;
|
|
}
|
|
#endif /* EXTATTR */
|
|
|
|
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
|
|
*/
|
|
static bool_t
|
|
proc_hardlinks_cb( void *contextp, nh_t hardheadh )
|
|
{
|
|
phcb_t *phcbp = ( phcb_t * )contextp;
|
|
node_t *hardheadp;
|
|
ino64_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 && ! isselpr
|
|
||
|
|
! isrealpr && ! isrefpr && isselpr ) {
|
|
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
|
|
||
|
|
isrealpr && isrefpr && ! isselpr
|
|
||
|
|
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;
|
|
}
|
|
|
|
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.
|
|
*/
|
|
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;
|
|
ok = tree_setattr_recurse( persp->p_rooth, path );
|
|
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;
|
|
/* REFERENCED */
|
|
struct fsxattr fsxattr; /* can we get rid of this? */
|
|
intgen_t rval;
|
|
void *hanp;
|
|
size_t hlen;
|
|
|
|
fsxattr.fsx_xflags = dirattr_get_xflags( dah );
|
|
fsxattr.fsx_extsize = dirattr_get_extsize( dah );
|
|
|
|
#ifdef BANYAN
|
|
#ifdef F_FSSETDM
|
|
if ( persp->p_restoredmpr ) {
|
|
fsdmidata_t fssetdm;
|
|
intgen_t fd;
|
|
|
|
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.
|
|
*/
|
|
if (path_to_handle(path, &hanp, &hlen)) {
|
|
mlog( MLOG_NORMAL | MLOG_WARNING,
|
|
"path_to_handle of %s failed:%s\n",
|
|
path, strerror( errno ));
|
|
return;
|
|
}
|
|
|
|
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);
|
|
return;
|
|
}
|
|
free_handle(hanp, hlen);
|
|
rval = fcntl( fd,
|
|
F_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 ));
|
|
}
|
|
( void )close( fd );
|
|
}
|
|
#endif /* F_FSSETDM */
|
|
#endif /* BANYAN */
|
|
|
|
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 );
|
|
rval = chmod( path, mode );
|
|
if ( rval ) {
|
|
mlog( MLOG_NORMAL | MLOG_TREE,
|
|
"chmod %s failed: %s\n",
|
|
path,
|
|
strerror( errno ));
|
|
}
|
|
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 ));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
|
|
bool_t quitnow;
|
|
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 );
|
|
|
|
if ( cmdp == tsi_cmd_quit ) {
|
|
quitnow = BOOL_TRUE;
|
|
} else {
|
|
quitnow = BOOL_FALSE;
|
|
}
|
|
postamblecnt = 0;
|
|
fold_init( fold, "end dialog", '-' );
|
|
postamblestr[ postamblecnt++ ] = "\n";
|
|
postamblestr[ postamblecnt++ ] = fold;
|
|
postamblestr[ postamblecnt++ ] = "\n\n";
|
|
assert( postamblecnt <= POSTAMBLEMAX );
|
|
dlog_end( postamblestr, postamblecnt );
|
|
|
|
return ! quitnow;
|
|
}
|
|
|
|
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;
|
|
ino64_t ino;
|
|
bool_t isdirpr;
|
|
bool_t isselpr;
|
|
|
|
/* walk the tree according to the path argument, to get
|
|
* the named node.
|
|
*/
|
|
ok = tsi_walkpath( arg,
|
|
persp->p_rooth,
|
|
tranp->t_inter.i_cwdh,
|
|
pcb,
|
|
pctxp,
|
|
&namedh,
|
|
&parh,
|
|
&cldh,
|
|
&ino,
|
|
&isdirpr,
|
|
&isselpr );
|
|
if ( ! ok ) {
|
|
return;
|
|
}
|
|
|
|
/* if named is not a dir, just display named
|
|
*/
|
|
if ( ! isdirpr ) {
|
|
( * pcb )( pctxp,
|
|
" %s %10llu %s%s\n",
|
|
isselpr ? "*" : " ",
|
|
ino,
|
|
tranp->t_inter.i_name,
|
|
isdirpr ? "/" : " " );
|
|
|
|
return;
|
|
}
|
|
|
|
/* iterate through the directory, printing all matching entries.
|
|
* hide the orphanage.
|
|
*/
|
|
while ( cldh != NH_NULL ) {
|
|
node_t *cldp;
|
|
nrh_t nrh;
|
|
nh_t nextcldh;
|
|
cldp = Node_map( cldh );
|
|
nrh = cldp->n_nrh;
|
|
nextcldh = cldp->n_sibh;
|
|
isdirpr = ( cldp->n_flags & NF_ISDIR );
|
|
isselpr = ( cldp->n_flags & NF_SUBTREE );
|
|
ino = cldp->n_ino;
|
|
Node_unmap( cldh, &cldp );
|
|
if ( cldh != persp->p_orphh ) {
|
|
/* REFERENCED */
|
|
intgen_t namelen;
|
|
namelen = namreg_get( nrh,
|
|
tranp->t_inter.i_name,
|
|
sizeof( tranp->t_inter.i_name ));
|
|
assert( namelen > 0 );
|
|
( * pcb )( pctxp,
|
|
" %s %10llu %s%s\n",
|
|
isselpr ? "*" : " ",
|
|
ino,
|
|
tranp->t_inter.i_name,
|
|
isdirpr ? "/" : " " );
|
|
}
|
|
cldh = nextcldh;
|
|
}
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
static void
|
|
tsi_cmd_cd( 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;
|
|
|
|
nh_t cldh;
|
|
nh_t parh;
|
|
nh_t namedh;
|
|
ino64_t ino;
|
|
bool_t isdirpr;
|
|
bool_t isselpr;
|
|
bool_t ok;
|
|
|
|
/* walk the tree according to the path argument, to get
|
|
* the named node.
|
|
*/
|
|
ok = tsi_walkpath( arg,
|
|
persp->p_rooth,
|
|
tranp->t_inter.i_cwdh,
|
|
pcb,
|
|
pctxp,
|
|
&namedh,
|
|
&parh,
|
|
&cldh,
|
|
&ino,
|
|
&isdirpr,
|
|
&isselpr );
|
|
if ( ! ok ) {
|
|
return;
|
|
}
|
|
|
|
/* if named is not a dir, complain
|
|
*/
|
|
if ( ! isdirpr ) {
|
|
assert( arg );
|
|
( * pcb )( pctxp,
|
|
"%s is not a directory\n",
|
|
arg );
|
|
|
|
return;
|
|
}
|
|
|
|
/* change the current working directory
|
|
*/
|
|
tranp->t_inter.i_cwdh = namedh;
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
static void
|
|
tsi_cmd_add( 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;
|
|
|
|
nh_t cldh;
|
|
nh_t parh;
|
|
nh_t namedh;
|
|
ino64_t ino;
|
|
bool_t isdirpr;
|
|
bool_t isselpr;
|
|
bool_t ok;
|
|
|
|
/* walk the tree according to the path argument, to get
|
|
* the named node.
|
|
*/
|
|
ok = tsi_walkpath( arg,
|
|
persp->p_rooth,
|
|
tranp->t_inter.i_cwdh,
|
|
pcb,
|
|
pctxp,
|
|
&namedh,
|
|
&parh,
|
|
&cldh,
|
|
&ino,
|
|
&isdirpr,
|
|
&isselpr );
|
|
if ( ! ok ) {
|
|
return;
|
|
}
|
|
|
|
selsubtree( namedh, BOOL_TRUE );
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
static void
|
|
tsi_cmd_delete( 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;
|
|
|
|
nh_t cldh;
|
|
nh_t parh;
|
|
nh_t namedh;
|
|
ino64_t ino;
|
|
bool_t isdirpr;
|
|
bool_t isselpr;
|
|
bool_t ok;
|
|
|
|
/* walk the tree according to the path argument, to get
|
|
* the named node.
|
|
*/
|
|
ok = tsi_walkpath( arg,
|
|
persp->p_rooth,
|
|
tranp->t_inter.i_cwdh,
|
|
pcb,
|
|
pctxp,
|
|
&namedh,
|
|
&parh,
|
|
&cldh,
|
|
&ino,
|
|
&isdirpr,
|
|
&isselpr );
|
|
if ( ! ok ) {
|
|
return;
|
|
}
|
|
|
|
selsubtree( namedh, BOOL_FALSE );
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
static void
|
|
tsi_cmd_extract( void *ctxp,
|
|
dlog_pcbp_t pcb,
|
|
void *pctxp )
|
|
{
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
static void
|
|
tsi_cmd_quit( void *ctxp,
|
|
dlog_pcbp_t pcb,
|
|
void *pctxp )
|
|
{
|
|
}
|
|
|
|
#ifdef WHITEPARSE
|
|
|
|
static int parse( int slotcnt, char **slotbuf, char *string );
|
|
|
|
static void
|
|
tsi_cmd_parse( char *buf )
|
|
{
|
|
int wordcnt;
|
|
|
|
if ( ! buf ) {
|
|
tranp->t_inter.i_argc = 0;
|
|
return;
|
|
}
|
|
|
|
wordcnt = parse( INTER_ARGMAX, tranp->t_inter.i_argv, buf );
|
|
|
|
tranp->t_inter.i_argc = ( size_t )min( max( 0, wordcnt ), INTER_ARGMAX );
|
|
}
|
|
|
|
#else /* WHITEPARSE */
|
|
|
|
static void
|
|
tsi_cmd_parse( char *buf )
|
|
{
|
|
size_t argc;
|
|
char *t;
|
|
char *b;
|
|
|
|
if ( ! buf ) {
|
|
tranp->t_inter.i_argc = 0;
|
|
return;
|
|
}
|
|
|
|
argc = 0;
|
|
b = buf;
|
|
while ( argc < INTER_ARGMAX && ( t = strtok( b, " \t" )) != 0 ) {
|
|
tranp->t_inter.i_argv[ argc++ ] = t;
|
|
b = 0;
|
|
}
|
|
tranp->t_inter.i_argc = argc;
|
|
}
|
|
|
|
#endif /* WHITEPARSE */
|
|
|
|
struct tsi_cmd_tbl {
|
|
char *tct_pattern;
|
|
char *tct_help;
|
|
dlog_ucbp_t tct_ucb;
|
|
size_t tct_argcmin;
|
|
size_t tct_argcmax;
|
|
};
|
|
|
|
typedef struct tsi_cmd_tbl tsi_cmd_tbl_t;
|
|
|
|
static tsi_cmd_tbl_t tsi_cmd_tbl[] = {
|
|
{ "pwd", "", tsi_cmd_pwd, 1, 1 },
|
|
{ "ls", "[ <path> ]", tsi_cmd_ls, 1, 2 },
|
|
{ "cd", "[ <path> ]", tsi_cmd_cd, 1, 2 },
|
|
{ "add", "[ <path> ]", tsi_cmd_add, 1, 2 },
|
|
{ "delete", "[ <path> ]", tsi_cmd_delete, 1, 2 },
|
|
{ "extract", "", tsi_cmd_extract,1, 1 },
|
|
{ "quit", "", tsi_cmd_quit, 1, 1 },
|
|
{ "help", "", tsi_cmd_help, 1, 1 },
|
|
};
|
|
|
|
static dlog_ucbp_t
|
|
tsi_cmd_match( void )
|
|
{
|
|
tsi_cmd_tbl_t *tblp = tsi_cmd_tbl;
|
|
tsi_cmd_tbl_t *tblendp = tsi_cmd_tbl
|
|
+
|
|
sizeof( tsi_cmd_tbl )
|
|
/
|
|
sizeof( tsi_cmd_tbl[ 0 ] );
|
|
|
|
if ( tranp->t_inter.i_argc == 0 ) {
|
|
return 0;
|
|
}
|
|
|
|
for ( ; tblp < tblendp ; tblp++ ) {
|
|
if ( ! strncmp( tranp->t_inter.i_argv[ 0 ],
|
|
tblp->tct_pattern,
|
|
strlen( tranp->t_inter.i_argv[ 0 ] ))) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( tblp == tblendp ) {
|
|
return 0;
|
|
}
|
|
|
|
assert( tblp->tct_argcmin != 0 );
|
|
if ( tranp->t_inter.i_argc < tblp->tct_argcmin ) {
|
|
return 0;
|
|
}
|
|
|
|
if ( tranp->t_inter.i_argc > tblp->tct_argcmax ) {
|
|
return 0;
|
|
}
|
|
|
|
return tblp->tct_ucb;
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
static void
|
|
tsi_cmd_help( void *ctxp,
|
|
dlog_pcbp_t pcb,
|
|
void *pctxp )
|
|
{
|
|
tsi_cmd_tbl_t *tblp = tsi_cmd_tbl;
|
|
tsi_cmd_tbl_t *tblendp = tsi_cmd_tbl
|
|
+
|
|
sizeof( tsi_cmd_tbl )
|
|
/
|
|
sizeof( tsi_cmd_tbl[ 0 ] );
|
|
|
|
( * pcb )( pctxp, "the following commands are available:\n" );
|
|
for ( ; tblp < tblendp ; tblp++ ) {
|
|
( * pcb )( pctxp,
|
|
"\t%s %s\n",
|
|
tblp->tct_pattern,
|
|
tblp->tct_help );
|
|
}
|
|
}
|
|
|
|
/* walks the tree following the given path.
|
|
* returns FALSE if syntax error encountered.
|
|
* returns by reference handles to the named node, its parent,
|
|
* the first child in its cld list, its ino, if it is a directory,
|
|
* and if it is selected.
|
|
* optionally given a dlog print func and context, to be used for diag output.
|
|
*/
|
|
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,
|
|
ino64_t *inop, bool_t *isdirprp, bool_t *isselprp )
|
|
{
|
|
nh_t namedh;
|
|
nh_t parh;
|
|
nh_t cldh;
|
|
node_t *namedp;
|
|
char *path;
|
|
char nbuf[ MAXNAMELEN ];
|
|
ino64_t ino;
|
|
bool_t isdirpr;
|
|
bool_t isselpr;
|
|
|
|
|
|
/* correct arg if ends with slash (if arg is "/", ok)
|
|
*/
|
|
if ( arg && strlen( arg ) > 1 && arg[ strlen( arg ) - 1 ] == '/' ) {
|
|
arg[ strlen( arg ) - 1 ] = 0;
|
|
}
|
|
|
|
/* use path to walk down the path argument
|
|
*/
|
|
path = arg;
|
|
|
|
/* walk the tree beginning either at the root node
|
|
* or at the current working directory
|
|
*/
|
|
if ( path && *path == '/' ) {
|
|
assert( rooth != NH_NULL );
|
|
namedh = rooth;
|
|
*path++;
|
|
} else {
|
|
assert( cwdh != NH_NULL );
|
|
namedh = cwdh;
|
|
}
|
|
|
|
/* get the parent of the starting point, and its cld list
|
|
*/
|
|
namedp = Node_map( namedh );
|
|
parh = namedp->n_parh;
|
|
cldh = namedp->n_cldh;
|
|
ino = namedp->n_ino;
|
|
isselpr = ( namedp->n_flags & NF_SUBTREE );
|
|
assert( namedp->n_flags & NF_ISDIR );
|
|
Node_unmap( namedh, &namedp );
|
|
isdirpr = BOOL_TRUE;
|
|
|
|
/* walk the tree from the starting point following the path arg.
|
|
* on leaving this loop, the following will be valid:
|
|
* namedh - the handle of the node named by path arg;
|
|
* parh - the parent of the named node;
|
|
* isdirpr - TRUE if named node is a directory;
|
|
* cldh - the first child in the named node's cld list.
|
|
*/
|
|
for ( ; ; ) {
|
|
size_t namelen;
|
|
char *strpatchp;
|
|
nh_t sibh;
|
|
|
|
/* if no path arg, break
|
|
*/
|
|
if ( ! path ) {
|
|
break;
|
|
}
|
|
|
|
/* clean out leading slashes. these can occur if the
|
|
* path arg is ".../////..." or "////..."
|
|
*/
|
|
while ( *path == '/' ) {
|
|
path++;
|
|
}
|
|
|
|
/* if empty path arg, break
|
|
*/
|
|
if ( ! strlen( path )) {
|
|
break;
|
|
}
|
|
|
|
/* copy the first name from the path, and advance
|
|
* the path pointer.
|
|
*/
|
|
namelen = strcspn( path, "/" );
|
|
assert( namelen < sizeof( nbuf ));
|
|
strncpy( nbuf, path, namelen );
|
|
nbuf[ namelen ] = 0;
|
|
path += namelen;
|
|
if ( *path ) {
|
|
assert( *path == '/' );
|
|
strpatchp = path;
|
|
*strpatchp = 0;
|
|
path++;
|
|
} else {
|
|
strpatchp = 0;
|
|
}
|
|
|
|
/* be sure the named node is a dir
|
|
*/
|
|
if ( ! isdirpr ) {
|
|
if ( pcb ) {
|
|
( * pcb )( pctxp,
|
|
"parent of %s is not a directory\n",
|
|
arg );
|
|
}
|
|
return BOOL_FALSE;
|
|
}
|
|
|
|
/* special case "."
|
|
*/
|
|
if ( ! strcmp( nbuf, "." )) {
|
|
if ( strpatchp ) {
|
|
*strpatchp = '/';
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* special case ".."
|
|
*/
|
|
if ( ! strcmp( nbuf, ".." )) {
|
|
if ( parh == NH_NULL ) {
|
|
if ( pcb ) {
|
|
( * pcb )( pctxp,
|
|
"%s above root\n",
|
|
arg );
|
|
}
|
|
return BOOL_FALSE;
|
|
}
|
|
namedh = parh;
|
|
namedp = Node_map( namedh );
|
|
parh = namedp->n_parh;
|
|
cldh = namedp->n_cldh;
|
|
ino = namedp->n_ino;
|
|
isselpr = ( namedp->n_flags & NF_SUBTREE );
|
|
Node_unmap( namedh, &namedp );
|
|
if ( strpatchp ) {
|
|
*strpatchp = '/';
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* look for child with right name
|
|
*/
|
|
sibh = cldh;
|
|
while ( sibh != NH_NULL ) {
|
|
node_t *sibp;
|
|
nh_t nextsibh;
|
|
nrh_t nrh;
|
|
/* REFERENCED */
|
|
intgen_t siblen;
|
|
|
|
sibp = Node_map( sibh );
|
|
nrh = sibp->n_nrh;
|
|
nextsibh = sibp->n_sibh;
|
|
cldh = sibp->n_cldh;
|
|
ino = sibp->n_ino;
|
|
isselpr = ( sibp->n_flags & NF_SUBTREE );
|
|
isdirpr = ( sibp->n_flags & NF_ISDIR );
|
|
Node_unmap( sibh, &sibp );
|
|
assert( nrh != NRH_NULL || sibh == persp->p_orphh );
|
|
if ( nrh != NRH_NULL ) {
|
|
siblen = namreg_get( nrh,
|
|
tranp->t_inter.i_name,
|
|
sizeof( tranp->t_inter.i_name ));
|
|
assert( siblen > 0 );
|
|
if ( ! strcmp( nbuf, tranp->t_inter.i_name )) {
|
|
break;
|
|
}
|
|
}
|
|
sibh = nextsibh;
|
|
}
|
|
|
|
/* if no match, complain
|
|
*/
|
|
if ( sibh == NH_NULL ) {
|
|
if ( pcb ) {
|
|
( * pcb )( pctxp,
|
|
"%s not found\n",
|
|
arg );
|
|
}
|
|
return BOOL_FALSE;
|
|
}
|
|
|
|
/* continue search. cldh, ino and isdirpr
|
|
* set in inner loop above
|
|
*/
|
|
parh = namedh;
|
|
namedh = sibh;
|
|
if ( strpatchp ) {
|
|
*strpatchp = '/';
|
|
}
|
|
}
|
|
*namedhp = namedh;
|
|
*parhp = parh;
|
|
*cldhp = cldh;
|
|
*inop = ino;
|
|
*isdirprp = isdirpr;
|
|
*isselprp = isselpr;
|
|
|
|
return BOOL_TRUE;
|
|
}
|
|
|
|
/* Node abstraction *********************************************************/
|
|
|
|
static nh_t
|
|
Node_alloc( ino64_t ino, gen_t gen, nrh_t nrh, dah_t dah, size_t flags )
|
|
{
|
|
nh_t nh;
|
|
node_t *np;
|
|
|
|
nh = node_alloc( );
|
|
assert( nh != NH_NULL );
|
|
np = Node_map( nh );
|
|
np->n_ino = ino;
|
|
np->n_nrh = nrh;
|
|
np->n_dah = dah;
|
|
np->n_hashh = NH_NULL;
|
|
np->n_parh = NH_NULL;
|
|
np->n_sibh = NH_NULL;
|
|
np->n_cldh = NH_NULL;
|
|
np->n_lnkh = NH_NULL;
|
|
np->n_gen = gen;
|
|
np->n_flags = ( u_char_t )flags;
|
|
Node_unmap( nh, &np );
|
|
return nh;
|
|
}
|
|
|
|
static void
|
|
Node_free( nh_t *nhp )
|
|
{
|
|
node_t *np;
|
|
np = Node_map( *nhp );
|
|
np->n_ino = 0;
|
|
np->n_gen = 0;
|
|
if ( np->n_nrh != NRH_NULL ) {
|
|
namreg_del( np->n_nrh );
|
|
np->n_nrh = NRH_NULL;
|
|
}
|
|
if ( np->n_dah != NRH_NULL ) {
|
|
dirattr_del( np->n_dah );
|
|
np->n_dah = NRH_NULL;
|
|
}
|
|
np->n_flags = 0;
|
|
np->n_parh = NH_NULL;
|
|
np->n_sibh = NH_NULL;
|
|
np->n_cldh = NH_NULL;
|
|
np->n_lnkh = NH_NULL;
|
|
Node_unmap( *nhp, &np );
|
|
node_free( nhp );
|
|
}
|
|
|
|
static node_t *
|
|
Node_map( nh_t nh )
|
|
{
|
|
return ( node_t * )node_map( nh );
|
|
}
|
|
|
|
static void
|
|
Node_unmap( nh_t nh, node_t **npp )
|
|
{
|
|
node_unmap( nh, ( void ** )npp );
|
|
}
|
|
|
|
/* builds a pathname for the specified node, relative to root
|
|
* returns FALSE if pathname too long
|
|
*/
|
|
static bool_t
|
|
Node2path( nh_t nh, char *path, char *errmsg )
|
|
{
|
|
intgen_t remainingcnt;
|
|
path[ 0 ] = 0; /* in case root node passed in */
|
|
remainingcnt = Node2path_recurse( nh, path, MAXPATHLEN );
|
|
if ( remainingcnt <= 0 ) {
|
|
node_t *np = Node_map( nh );
|
|
ino64_t ino = np->n_ino;
|
|
gen_t gen = np->n_gen;
|
|
Node_unmap( nh, &np );
|
|
mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_TREE,
|
|
"unable %s ino %llu gen %u: "
|
|
"relative pathname too long (partial %s)\n",
|
|
errmsg,
|
|
ino,
|
|
gen,
|
|
path );
|
|
return BOOL_FALSE;
|
|
} else {
|
|
return BOOL_TRUE;
|
|
}
|
|
}
|
|
|
|
/* returns how much of the buffer remains, assuming the buffer size is
|
|
* MAXPATHLEN. always null-terminates, but null char not counted in return.
|
|
* works because the buffer size is secretly 2 * MAXPATHLEN.
|
|
*/
|
|
static intgen_t
|
|
Node2path_recurse( nh_t nh, char *buf, intgen_t bufsz )
|
|
{
|
|
node_t *np;
|
|
nh_t parh;
|
|
ino64_t ino;
|
|
gen_t gen;
|
|
nrh_t nrh;
|
|
intgen_t oldbufsz;
|
|
intgen_t namelen;
|
|
|
|
/* recursion termination
|
|
*/
|
|
if ( nh == persp->p_rooth ) {
|
|
return bufsz;
|
|
}
|
|
|
|
/* extract useful node members
|
|
*/
|
|
np = Node_map( nh );
|
|
parh = np->n_parh;
|
|
ino = np->n_ino;
|
|
gen = np->n_gen;
|
|
nrh = np->n_nrh;
|
|
Node_unmap( nh, &np );
|
|
|
|
/* build path to parent
|
|
*/
|
|
oldbufsz = bufsz;
|
|
bufsz = Node2path_recurse( parh, buf, bufsz ); /* RECURSION */
|
|
if ( bufsz <= 0 ) {
|
|
return bufsz;
|
|
}
|
|
buf += oldbufsz - bufsz;
|
|
|
|
/* insert slash if parent not root
|
|
*/
|
|
if ( parh != persp->p_rooth ) {
|
|
assert( bufsz + MAXPATHLEN >= 2 );
|
|
*buf++ = '/';
|
|
*( buf + 1 ) = 0;
|
|
bufsz--;
|
|
if ( bufsz <= 0 ) {
|
|
return bufsz;
|
|
}
|
|
}
|
|
|
|
/* append entry name: special case if in orphanage
|
|
*/
|
|
if ( parh == persp->p_orphh ) {
|
|
namelen = sprintf( buf, "%llu.%u", ino, gen );
|
|
} else if ( nh == persp->p_orphh ) {
|
|
namelen = sprintf( buf, "%s", orphname );
|
|
} else {
|
|
assert( nrh != NRH_NULL );
|
|
namelen = namreg_get( nrh, buf, ( size_t )bufsz + MAXPATHLEN );
|
|
assert( namelen > 0 );
|
|
}
|
|
|
|
/* return remaining buffer size
|
|
*/
|
|
bufsz -= namelen;
|
|
assert( bufsz + MAXPATHLEN > 0 );
|
|
return bufsz;
|
|
}
|
|
|
|
/* family abstraction *********************************************************/
|
|
|
|
static void
|
|
adopt( nh_t parh, nh_t cldh, nrh_t nrh )
|
|
{
|
|
node_t *parp;
|
|
node_t *cldp;
|
|
|
|
cldp = Node_map( cldh );
|
|
cldp->n_parh = parh;
|
|
cldp->n_nrh = nrh;
|
|
parp = Node_map( parh );
|
|
cldp->n_sibh = parp->n_cldh;
|
|
Node_unmap( cldh, &cldp );
|
|
parp->n_cldh = cldh;
|
|
Node_unmap( parh, &parp );
|
|
}
|
|
|
|
static nrh_t
|
|
disown( nh_t cldh )
|
|
{
|
|
node_t *cldp;
|
|
nrh_t nrh;
|
|
nh_t parh;
|
|
node_t *parp;
|
|
|
|
cldp = Node_map( cldh );
|
|
|
|
nrh = cldp->n_nrh;
|
|
|
|
parh = cldp->n_parh;
|
|
assert( parh != NH_NULL );
|
|
if ( parh == NH_NULL ) {
|
|
mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_TREE,
|
|
"attempt to disown child "
|
|
"which has no parent!\n" );
|
|
return nrh;
|
|
}
|
|
parp = Node_map( parh );
|
|
assert( parp->n_cldh != NH_NULL );
|
|
if ( parp->n_cldh == NH_NULL ) {
|
|
mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_TREE,
|
|
"attempt to disown child "
|
|
"when parent has no children!\n" );
|
|
return nrh;
|
|
}
|
|
if ( parp->n_cldh == cldh ) {
|
|
parp->n_cldh = cldp->n_sibh;
|
|
} else {
|
|
nh_t prevcldh = parp->n_cldh;
|
|
node_t *prevcldp = Node_map( prevcldh );
|
|
while ( prevcldp->n_sibh != NH_NULL
|
|
&&
|
|
prevcldp->n_sibh != cldh ) {
|
|
nh_t tmph = prevcldp->n_sibh;
|
|
Node_unmap( prevcldh, &prevcldp );
|
|
prevcldh = tmph;
|
|
prevcldp = Node_map( prevcldh );
|
|
}
|
|
assert( prevcldp->n_sibh != NH_NULL );
|
|
if ( prevcldp->n_sibh == NH_NULL ) {
|
|
mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_TREE,
|
|
"attempt to disown child "
|
|
"with wrong parent!\n" );
|
|
return nrh;
|
|
}
|
|
prevcldp->n_sibh = cldp->n_sibh;
|
|
Node_unmap( prevcldh, &prevcldp );
|
|
}
|
|
Node_unmap( parh, &parp );
|
|
cldp->n_parh = NH_NULL;
|
|
cldp->n_sibh = NH_NULL;
|
|
Node_unmap( cldh, &cldp );
|
|
|
|
return nrh;
|
|
}
|
|
|
|
/* recursively marks all nodes in subtree as selected or not selected
|
|
* for subtree restoral. adjusts ancestors flags accordingly. also adjusts
|
|
* inomap, which will be used by content.c to see if a media file contains
|
|
* any nondirs which might need to be restored.
|
|
*/
|
|
static void
|
|
selsubtree( nh_t nh, bool_t sensepr )
|
|
{
|
|
node_t *np;
|
|
nh_t parh;
|
|
|
|
/* first mark the subtree
|
|
*/
|
|
selsubtree_recurse_down( nh, sensepr );
|
|
|
|
/* get parent
|
|
*/
|
|
np = Node_map( nh );
|
|
parh = np->n_parh;
|
|
Node_unmap( nh, &np );
|
|
|
|
/* next adjust ancestory
|
|
*/
|
|
while ( parh != NH_NULL ) {
|
|
node_t *parp;
|
|
nh_t newparh;
|
|
|
|
parp = Node_map( parh );
|
|
if ( sensepr ) {
|
|
parp->n_flags |= NF_SUBTREE;
|
|
} else {
|
|
bool_t atleastonechildselpr = BOOL_FALSE;
|
|
nh_t cldh = parp->n_cldh;
|
|
while ( cldh != NH_NULL ) {
|
|
node_t *cldp;
|
|
nh_t nextcldh;
|
|
bool_t cldsensepr;
|
|
cldp = Node_map( cldh );
|
|
cldsensepr = ( cldp->n_flags & NF_SUBTREE )
|
|
?
|
|
BOOL_TRUE
|
|
:
|
|
BOOL_FALSE;
|
|
nextcldh = cldp->n_sibh;
|
|
Node_unmap( cldh, &cldp );
|
|
if ( cldsensepr ) {
|
|
atleastonechildselpr = BOOL_TRUE;
|
|
break;
|
|
}
|
|
cldh = nextcldh;
|
|
}
|
|
if ( ! atleastonechildselpr ) {
|
|
parp->n_flags &= ~NF_SUBTREE;
|
|
/* DBG could break out here (remember to unmap!)
|
|
*/
|
|
}
|
|
}
|
|
newparh = parp->n_parh;
|
|
Node_unmap( parh, &parp );
|
|
parh = newparh;
|
|
}
|
|
}
|
|
|
|
static void
|
|
selsubtree_recurse_down( nh_t nh, bool_t sensepr )
|
|
{
|
|
nh_t cldh;
|
|
|
|
/* first mark the node indicated, and get head of cld list
|
|
*/
|
|
{
|
|
node_t *np;
|
|
bool_t isdirpr;
|
|
ino64_t ino;
|
|
gen_t gen;
|
|
|
|
np = Node_map( nh );
|
|
if ( sensepr ) {
|
|
np->n_flags |= NF_SUBTREE;
|
|
} else {
|
|
np->n_flags &= ~NF_SUBTREE;
|
|
}
|
|
cldh = np->n_cldh;
|
|
ino = np->n_ino;
|
|
gen = np->n_gen;
|
|
isdirpr = ( np->n_flags & NF_ISDIR );
|
|
Node_unmap( nh, &np );
|
|
if ( ! isdirpr ) {
|
|
if ( sensepr ) {
|
|
inomap_rst_add( ino );
|
|
} else {
|
|
/* check hardlist: don't del unless none needed
|
|
*/
|
|
nh_t nh;
|
|
bool_t neededpr = BOOL_FALSE;
|
|
for ( nh = link_hardh( ino, gen )
|
|
;
|
|
nh != NH_NULL
|
|
;
|
|
nh = link_nexth( nh )) {
|
|
node_t *np;
|
|
u_char_t flags;
|
|
np = Node_map( nh );
|
|
flags = np->n_flags;
|
|
Node_unmap( nh, &np );
|
|
if ( flags & NF_SUBTREE ) {
|
|
neededpr = BOOL_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if ( ! neededpr ) {
|
|
inomap_rst_del( ino );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* then mark all of its children. be sure to skip the orphanage!!!
|
|
*/
|
|
while ( cldh != NH_NULL ) {
|
|
node_t *cldp;
|
|
nh_t nextcldh;
|
|
|
|
if ( cldh != persp->p_orphh ) {
|
|
selsubtree_recurse_down( cldh, sensepr );
|
|
}
|
|
cldp = Node_map( cldh );
|
|
nextcldh = cldp->n_sibh;
|
|
Node_unmap( cldh, &cldp );
|
|
cldh = nextcldh;
|
|
}
|
|
}
|
|
|
|
|
|
/* link abstraction *********************************************************/
|
|
|
|
/* returns handle to head of hard link list
|
|
*/
|
|
static nh_t
|
|
link_hardh( ino64_t ino, gen_t gen )
|
|
{
|
|
return hash_find( ino, gen );
|
|
}
|
|
|
|
/* returns following node in hard link list
|
|
*/
|
|
static nh_t
|
|
link_nexth( nh_t nh )
|
|
{
|
|
node_t *np;
|
|
nh_t nexth;
|
|
|
|
np = Node_map( nh );
|
|
nexth = np->n_lnkh;
|
|
Node_unmap( nh, &np );
|
|
return nexth;
|
|
}
|
|
|
|
/* searches hard link list for exact match.
|
|
* returns hard link list head
|
|
*/
|
|
static nh_t
|
|
link_matchh( nh_t hardh, nh_t parh, char *name )
|
|
{
|
|
while ( hardh != NH_NULL ) {
|
|
node_t *np;
|
|
nh_t nexth;
|
|
np = Node_map( hardh );
|
|
if ( np->n_parh == parh ) {
|
|
/* REFERENCED */
|
|
intgen_t namelen;
|
|
namelen = namreg_get( np->n_nrh,
|
|
tranp->t_namebuf,
|
|
sizeof( tranp->t_namebuf ));
|
|
assert( namelen > 0 );
|
|
if ( ! strcmp( name, tranp->t_namebuf )) {
|
|
Node_unmap( hardh, &np );
|
|
break;
|
|
}
|
|
}
|
|
nexth = np->n_lnkh;
|
|
Node_unmap( hardh, &np );
|
|
hardh = nexth;
|
|
}
|
|
return hardh;
|
|
}
|
|
|
|
static void
|
|
link_in( nh_t nh )
|
|
{
|
|
node_t *np;
|
|
ino64_t ino;
|
|
gen_t gen;
|
|
nh_t hardh;
|
|
|
|
/* map in the node to read ino and gen
|
|
*/
|
|
np = Node_map( nh );
|
|
ino = np->n_ino;
|
|
gen = np->n_gen;
|
|
|
|
/* see if one or more links already hashed.
|
|
*/
|
|
hardh = hash_find( ino, gen );
|
|
|
|
/* if not hashed, just hash it. otherwise put at end
|
|
* of hard link (lnk) list.
|
|
*/
|
|
if ( hardh == NH_NULL ) {
|
|
hash_in( nh );
|
|
} else {
|
|
nh_t prevh = hardh;
|
|
node_t *prevp = Node_map( prevh );
|
|
while ( prevp->n_lnkh != NH_NULL ) {
|
|
nh_t nexth = prevp->n_lnkh;
|
|
Node_unmap( prevh, &prevp );
|
|
prevh = nexth;
|
|
prevp = Node_map( prevh );
|
|
}
|
|
prevp->n_lnkh = nh;
|
|
Node_unmap( prevh, &prevp );
|
|
}
|
|
|
|
/* since always put at end of hard link list, make node's
|
|
* lnk member terminate list.
|
|
*/
|
|
np->n_lnkh = NH_NULL;
|
|
Node_unmap( nh, &np );
|
|
}
|
|
|
|
static void
|
|
link_out( nh_t nh )
|
|
{
|
|
node_t *np;
|
|
ino64_t ino;
|
|
gen_t gen;
|
|
nh_t hardh;
|
|
|
|
/* map in the node to read ino and gen
|
|
*/
|
|
np = Node_map( nh );
|
|
ino = np->n_ino;
|
|
gen = np->n_gen;
|
|
|
|
/* get head of hard link list
|
|
*/
|
|
hardh = hash_find( ino, gen );
|
|
assert( hardh != NH_NULL );
|
|
|
|
/* if node is at head of hard link list, hash it out and
|
|
* hash in the following node in link list, if there is one.
|
|
* otherwise, unlink from hardlink list.
|
|
*/
|
|
if ( nh == hardh ) {
|
|
hash_out( nh );
|
|
if ( np->n_lnkh != NH_NULL ) {
|
|
hash_in( np->n_lnkh );
|
|
}
|
|
} else {
|
|
nh_t prevh = hardh;
|
|
node_t *prevp = Node_map( prevh );
|
|
while ( prevp->n_lnkh != nh ) {
|
|
nh_t nexth = prevp->n_lnkh;
|
|
Node_unmap( prevh, &prevp );
|
|
prevh = nexth;
|
|
assert( prevh != NH_NULL );
|
|
prevp = Node_map( prevh );
|
|
}
|
|
prevp->n_lnkh = np->n_lnkh;
|
|
Node_unmap( prevh, &prevp );
|
|
}
|
|
np->n_lnkh = NH_NULL;
|
|
|
|
/* release the mapping
|
|
*/
|
|
Node_unmap( nh, &np );
|
|
}
|
|
|
|
/* invokes callback for all hardheads
|
|
* iteration aborted if callback returns FALSE
|
|
*/
|
|
static void
|
|
link_headiter( bool_t ( * cbfp )( void *contextp, nh_t hardh ), void *contextp )
|
|
{
|
|
hash_iter( cbfp, contextp );
|
|
}
|
|
|
|
/* iterator for a hard link list. allows deletion of the last node
|
|
* returned.
|
|
*/
|
|
static void
|
|
link_iter_init( link_iter_context_t *link_iter_contextp, nh_t hardheadh )
|
|
{
|
|
link_iter_contextp->li_headh = hardheadh;
|
|
link_iter_contextp->li_prevh = NH_NULL;
|
|
link_iter_contextp->li_lasth = NH_NULL;
|
|
link_iter_contextp->li_donepr = BOOL_FALSE;
|
|
}
|
|
|
|
static nh_t
|
|
link_iter_next( link_iter_context_t *link_iter_contextp )
|
|
{
|
|
node_t *lastp;
|
|
nh_t tmplasth;
|
|
|
|
/* if already done, return
|
|
*/
|
|
if ( link_iter_contextp->li_donepr == BOOL_TRUE ) {
|
|
return NH_NULL;
|
|
}
|
|
|
|
/* if no hardhead, done
|
|
*/
|
|
if ( link_iter_contextp->li_headh == NH_NULL ) {
|
|
link_iter_contextp->li_donepr = BOOL_TRUE;
|
|
return NH_NULL;
|
|
}
|
|
|
|
/* make tmp copy of last
|
|
*/
|
|
tmplasth = link_iter_contextp->li_lasth;
|
|
|
|
/* if no last, must be first call
|
|
*/
|
|
if ( tmplasth == NH_NULL ) {
|
|
assert( link_iter_contextp->li_prevh == NH_NULL );
|
|
link_iter_contextp->li_lasth = link_iter_contextp->li_headh;
|
|
return link_iter_contextp->li_lasth;
|
|
}
|
|
|
|
/* slide last into prev
|
|
*/
|
|
link_iter_contextp->li_prevh = tmplasth;
|
|
lastp = Node_map( tmplasth );
|
|
link_iter_contextp->li_lasth = lastp->n_lnkh;
|
|
Node_unmap( tmplasth, &lastp );
|
|
|
|
/* if NULL, flag done
|
|
*/
|
|
if ( link_iter_contextp->li_lasth == NH_NULL ) {
|
|
link_iter_contextp->li_donepr = BOOL_TRUE;
|
|
}
|
|
|
|
/* return the last handle
|
|
*/
|
|
return link_iter_contextp->li_lasth;
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
void
|
|
link_iter_unlink( link_iter_context_t *link_iter_contextp, nh_t nh )
|
|
{
|
|
node_t *lastp;
|
|
nh_t nexth;
|
|
|
|
/* sanity checks
|
|
*/
|
|
assert( link_iter_contextp->li_lasth != NH_NULL );
|
|
assert( nh == link_iter_contextp->li_lasth );
|
|
|
|
/* get the next node in list
|
|
*/
|
|
lastp = Node_map( link_iter_contextp->li_lasth );
|
|
nexth = lastp->n_lnkh;
|
|
lastp->n_lnkh = NH_NULL;
|
|
Node_unmap( link_iter_contextp->li_lasth, &lastp );
|
|
|
|
if ( link_iter_contextp->li_lasth == link_iter_contextp->li_headh ) {
|
|
assert( link_iter_contextp->li_prevh == NH_NULL );
|
|
hash_out( link_iter_contextp->li_headh );
|
|
link_iter_contextp->li_headh = nexth;
|
|
if ( nexth != NH_NULL ) {
|
|
hash_in( nexth );
|
|
}
|
|
} else {
|
|
node_t *prevp;
|
|
assert( link_iter_contextp->li_prevh != NH_NULL );
|
|
prevp = Node_map( link_iter_contextp->li_prevh );
|
|
prevp->n_lnkh = nexth;
|
|
Node_unmap( link_iter_contextp->li_prevh, &prevp );
|
|
}
|
|
link_iter_contextp->li_lasth = link_iter_contextp->li_prevh;
|
|
link_iter_contextp->li_prevh = NH_NULL;
|
|
}
|
|
|
|
|
|
/* hash abstraction *********************************************************/
|
|
|
|
#define HASHLEN_MIN ( pgsz / sizeof( nh_t ))
|
|
|
|
static bool_t
|
|
hash_init( size64_t vmsz,
|
|
size64_t dircnt,
|
|
size64_t nondircnt,
|
|
char *perspath )
|
|
{
|
|
size64_t hashlen;
|
|
size64_t loghashlen;
|
|
size64_t vmlen;
|
|
size64_t hashlenmax;
|
|
ix_t hix;
|
|
|
|
/* sanity checks
|
|
*/
|
|
assert( pgsz % sizeof( nh_t ) == 0 );
|
|
|
|
/* calculate the size of the hash array. must be a power of two,
|
|
* and a multiple of the page size. don't use more than the available
|
|
* vm. but enforce a minimum.
|
|
*/
|
|
vmlen = vmsz / sizeof( nh_t );
|
|
hashlenmax = min( vmlen, SIZEMAX );
|
|
hashlen = ( dircnt + nondircnt );
|
|
hashlen = max( hashlen, ( size64_t )HASHLEN_MIN );
|
|
hashlen = min( hashlen, hashlenmax );
|
|
|
|
for ( loghashlen = 0
|
|
;
|
|
( ( size64_t )1 << loghashlen ) <= hashlen
|
|
;
|
|
loghashlen++ )
|
|
;
|
|
assert( loghashlen > 0 );
|
|
loghashlen--;
|
|
hashlen = ( size64_t )1 << loghashlen;
|
|
assert( hashlen <= hashlenmax );
|
|
|
|
/* record hash size in persistent state
|
|
*/
|
|
persp->p_hashsz = hashlen * sizeof( nh_t );
|
|
|
|
/* map the hash array just after the persistent state header
|
|
*/
|
|
assert( persp->p_hashsz <= SIZEMAX );
|
|
assert( ! ( persp->p_hashsz % ( size64_t )pgsz ));
|
|
assert( ! ( PERSSZ % pgsz ));
|
|
tranp->t_hashp = ( nh_t * ) mmap64( 0,
|
|
( size_t )persp->p_hashsz,
|
|
PROT_READ | PROT_WRITE,
|
|
MAP_SHARED | MAP_AUTOGROW,
|
|
tranp->t_persfd,
|
|
( off64_t )PERSSZ );
|
|
if ( tranp->t_hashp == ( nh_t * )-1 ) {
|
|
mlog( MLOG_NORMAL | MLOG_TREE,
|
|
"unable to mmap hash array into %s: %s\n",
|
|
perspath,
|
|
strerror( errno ));
|
|
return BOOL_FALSE;
|
|
}
|
|
|
|
/* initialize the hash array to all NULL node handles
|
|
*/
|
|
for ( hix = 0 ; hix < ( ix_t )hashlen ; hix++ ) {
|
|
tranp->t_hashp[ hix ] = NH_NULL;
|
|
}
|
|
|
|
/* build a hash mask. this works because hashlen is a power of two.
|
|
* record in persistent state.
|
|
*/
|
|
assert( hashlen - 1 <= SIZEMAX );
|
|
persp->p_hashmask = ( size_t )( hashlen - 1 );
|
|
|
|
return BOOL_TRUE;
|
|
}
|
|
|
|
static bool_t
|
|
hash_sync( char *perspath )
|
|
{
|
|
size64_t hashsz;
|
|
|
|
/* sanity checks
|
|
*/
|
|
assert( pgsz % sizeof( nh_t ) == 0 );
|
|
|
|
/* retrieve the hash size from the persistent state
|
|
*/
|
|
hashsz = persp->p_hashsz;
|
|
assert( ! ( hashsz % sizeof( nh_t )));
|
|
|
|
/* map the hash array just after the persistent state header
|
|
*/
|
|
assert( hashsz <= SIZEMAX );
|
|
assert( ! ( hashsz % ( size64_t )pgsz ));
|
|
assert( ! ( PERSSZ % pgsz ));
|
|
tranp->t_hashp = ( nh_t * ) mmap64( 0,
|
|
( size_t )hashsz,
|
|
PROT_READ | PROT_WRITE,
|
|
MAP_SHARED | MAP_AUTOGROW,
|
|
tranp->t_persfd,
|
|
( off64_t )PERSSZ );
|
|
if ( tranp->t_hashp == ( nh_t * )-1 ) {
|
|
mlog( MLOG_NORMAL | MLOG_TREE,
|
|
"unable to mmap hash array into %s: %s\n",
|
|
perspath,
|
|
strerror( errno ));
|
|
return BOOL_FALSE;
|
|
}
|
|
|
|
return BOOL_TRUE;
|
|
}
|
|
|
|
static void
|
|
hash_in( nh_t nh )
|
|
{
|
|
node_t *np;
|
|
ino64_t ino;
|
|
size_t hix;
|
|
nh_t *entryp;
|
|
|
|
/* get a mapping to the node
|
|
*/
|
|
np = Node_map( nh );
|
|
|
|
/* get ino from node
|
|
*/
|
|
ino = np->n_ino;
|
|
|
|
/* assert not already in
|
|
*/
|
|
assert( hash_find( np->n_ino, np->n_gen ) == NH_NULL );
|
|
|
|
/* calculate the hash index
|
|
*/
|
|
hix = ( size_t )ino & persp->p_hashmask;
|
|
|
|
/* get a pointer to the indexed hash array entry
|
|
*/
|
|
entryp = &tranp->t_hashp[ hix ];
|
|
|
|
/* insert into the list, at the head
|
|
*/
|
|
assert( np->n_hashh == NH_NULL );
|
|
np->n_hashh = *entryp;
|
|
*entryp = nh;
|
|
|
|
/* release the mapping
|
|
*/
|
|
Node_unmap( nh, &np );
|
|
}
|
|
|
|
static void
|
|
hash_out( nh_t nh )
|
|
{
|
|
node_t *np;
|
|
ino64_t ino;
|
|
nh_t hashheadh;
|
|
size_t hix;
|
|
nh_t *entryp;
|
|
|
|
/* get a mapping to the node
|
|
*/
|
|
np = Node_map( nh );
|
|
|
|
/* get the ino
|
|
*/
|
|
ino = np->n_ino;
|
|
|
|
/* get a pointer to the hash array entry
|
|
*/
|
|
hix = ( size_t )ino & persp->p_hashmask;
|
|
entryp = &tranp->t_hashp[ hix ];
|
|
|
|
/* get the handle of the first node in the appropriate hash array
|
|
*/
|
|
hashheadh = *entryp;
|
|
assert( hashheadh != NH_NULL );
|
|
|
|
/* if node is first in list, replace entry with following node.
|
|
* otherwise, walk the list until found.
|
|
*/
|
|
if ( hashheadh == nh ) {
|
|
*entryp = np->n_hashh;
|
|
} else {
|
|
nh_t prevh = hashheadh;
|
|
node_t *prevp = Node_map( prevh );
|
|
while ( prevp->n_hashh != nh ) {
|
|
nh_t nexth = prevp->n_hashh;
|
|
Node_unmap( prevh, &prevp );
|
|
prevh = nexth;
|
|
assert( prevh != NH_NULL );
|
|
prevp = Node_map( prevh );
|
|
}
|
|
prevp->n_hashh = np->n_hashh;
|
|
Node_unmap( prevh, &prevp );
|
|
}
|
|
np->n_hashh = NH_NULL;
|
|
|
|
/* release the mapping
|
|
*/
|
|
Node_unmap( nh, &np );
|
|
}
|
|
|
|
static nh_t
|
|
hash_find( ino64_t ino, gen_t gen )
|
|
{
|
|
nh_t nh;
|
|
node_t *np;
|
|
size_t hix;
|
|
|
|
/* get handle to first node in appropriate hash array
|
|
*/
|
|
hix = ( size_t )ino & persp->p_hashmask;
|
|
nh = tranp->t_hashp[ hix ];
|
|
|
|
/* if list empty, return null handle
|
|
*/
|
|
if ( nh == NH_NULL ) {
|
|
return NH_NULL;
|
|
}
|
|
|
|
/* walk the list until found.
|
|
*/
|
|
np = Node_map( nh );
|
|
while ( np->n_ino != ino || np->n_gen != gen ) {
|
|
nh_t nextnh = np->n_hashh;
|
|
Node_unmap( nh, &np );
|
|
nh = nextnh;
|
|
if ( nh == NH_NULL ) {
|
|
return NH_NULL;
|
|
}
|
|
np = Node_map( nh );
|
|
}
|
|
Node_unmap( nh, &np );
|
|
|
|
return nh;
|
|
}
|
|
|
|
/* invokes callback for all hashed nodes
|
|
* iteration aborted if callback returns FALSE
|
|
* call back may hash out and free the node, so
|
|
* must figure next node prior to calling callback.
|
|
*/
|
|
static void
|
|
hash_iter( bool_t ( * cbfp )( void *contextp, nh_t hashh ), void *contextp )
|
|
{
|
|
ix_t hix;
|
|
size64_t hashlen = persp->p_hashsz / sizeof( nh_t );
|
|
|
|
for ( hix = 0 ; hix < ( ix_t )hashlen ; hix++ ) {
|
|
nh_t nh = tranp->t_hashp[ hix ];
|
|
|
|
while ( nh != NH_NULL ) {
|
|
node_t *np;
|
|
nh_t nexth;
|
|
bool_t ok;
|
|
|
|
np = Node_map( nh );
|
|
nexth = np->n_hashh;
|
|
Node_unmap( nh, &np );
|
|
|
|
ok = ( * cbfp )( contextp, nh );
|
|
if ( ! ok ) {
|
|
return;
|
|
}
|
|
|
|
nh = nexth;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* misc static functions *****************************************************/
|
|
|
|
#ifdef TREE_CHK
|
|
/* use hash array to iterate through all nodes. check
|
|
* each node's hash, hardlink, namreg, dirattr, parent,
|
|
* and sibling handles.
|
|
*/
|
|
static bool_t
|
|
Node_chk( nh_t nh, nh_t *nexthashhp, nh_t *nextlnkhp )
|
|
{
|
|
node_t *np;
|
|
node_t n;
|
|
char nambuf[ MAXNAMELEN + 1 ];
|
|
bool_t okaccum;
|
|
|
|
mlog( MLOG_NITTY + 1 | MLOG_TREE,
|
|
"checking node nh == 0x%x\n",
|
|
nh );
|
|
|
|
okaccum = BOOL_TRUE;
|
|
|
|
if ( nexthashhp ) {
|
|
*nexthashhp = NH_NULL;
|
|
}
|
|
|
|
assert( nextlnkhp );
|
|
*nextlnkhp = NH_NULL;
|
|
|
|
np = Node_map( nh );
|
|
assert( np );
|
|
n = *np;
|
|
Node_unmap( nh, &np );
|
|
|
|
if ( ! nexthashhp && n.n_hashh != NH_NULL ) {
|
|
mlog( MLOG_NORMAL | MLOG_ERROR | MLOG_TREE,
|
|
"nh 0x%x np 0x%x hash link not null\n",
|
|
nh,
|
|
np );
|
|
okaccum = BOOL_FALSE;
|
|
}
|
|
|
|
if ( n.n_hashh != NH_NULL ) {
|
|
np = Node_map( n.n_hashh );
|
|
Node_unmap( n.n_hashh, &np );
|
|
}
|
|
|
|
if ( n.n_lnkh != NH_NULL ) {
|
|
np = Node_map( n.n_lnkh );
|
|
Node_unmap( n.n_lnkh, &np );
|
|
}
|
|
|
|
if ( n.n_parh != NH_NULL ) {
|
|
np = Node_map( n.n_parh );
|
|
Node_unmap( n.n_parh, &np );
|
|
}
|
|
|
|
if ( n.n_cldh != NH_NULL ) {
|
|
np = Node_map( n.n_cldh );
|
|
Node_unmap( n.n_cldh, &np );
|
|
}
|
|
|
|
if ( n.n_sibh != NH_NULL ) {
|
|
np = Node_map( n.n_sibh );
|
|
Node_unmap( n.n_sibh, &np );
|
|
}
|
|
|
|
if ( n.n_nrh != NRH_NULL ) {
|
|
intgen_t rval;
|
|
rval = namreg_get( n.n_nrh, nambuf, sizeof( nambuf ));
|
|
assert( rval >= 0 );
|
|
}
|
|
|
|
if ( n.n_dah != DAH_NULL ) {
|
|
( void )dirattr_get_mode( n.n_dah );
|
|
}
|
|
|
|
if ( nexthashhp ) {
|
|
*nexthashhp = n.n_hashh;
|
|
}
|
|
|
|
*nextlnkhp = n.n_lnkh;
|
|
|
|
return okaccum;
|
|
}
|
|
|
|
bool_t
|
|
tree_chk( void )
|
|
{
|
|
ix_t hix;
|
|
size64_t hashlen = persp->p_hashsz / sizeof( nh_t );
|
|
bool_t ok;
|
|
bool_t okaccum;
|
|
|
|
okaccum = BOOL_TRUE;
|
|
|
|
for ( hix = 0 ; hix < ( ix_t )hashlen ; hix++ ) {
|
|
nh_t hashh = tranp->t_hashp[ hix ];
|
|
|
|
mlog( MLOG_NITTY + 1 | MLOG_TREE,
|
|
"checking hix %u\n",
|
|
hix );
|
|
while ( hashh != NH_NULL ) {
|
|
nh_t lnkh;
|
|
|
|
ok = Node_chk( hashh, &hashh, &lnkh );
|
|
if ( ! ok ) {
|
|
okaccum = BOOL_FALSE;
|
|
}
|
|
|
|
while ( lnkh != NH_NULL ) {
|
|
ok = Node_chk( lnkh, 0, &lnkh );
|
|
if ( ! ok ) {
|
|
okaccum = BOOL_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ok = tree_chk2( );
|
|
if ( ! ok ) {
|
|
okaccum = BOOL_FALSE;
|
|
}
|
|
|
|
return okaccum;
|
|
}
|
|
|
|
static bool_t tree_chk2_recurse( nh_t cldh, nh_t parh );
|
|
|
|
static bool_t
|
|
tree_chk2( void )
|
|
{
|
|
node_t *rootp;
|
|
nh_t cldh;
|
|
bool_t ok;
|
|
|
|
mlog( MLOG_DEBUG | MLOG_TREE,
|
|
"tree hierarchy check\n" );
|
|
|
|
rootp = Node_map( persp->p_rooth );
|
|
cldh = rootp->n_cldh;
|
|
Node_unmap( persp->p_rooth, &rootp );
|
|
|
|
ok = tree_chk2_recurse( cldh, persp->p_rooth );
|
|
|
|
return ok;
|
|
}
|
|
|
|
static bool_t
|
|
tree_chk2_recurse( nh_t cldh, nh_t parh )
|
|
{
|
|
bool_t okaccum = BOOL_TRUE;
|
|
|
|
assert( parh != NH_NULL );
|
|
|
|
while ( cldh != NH_NULL ) {
|
|
node_t *cldp;
|
|
ino64_t ino;
|
|
gen_t gen;
|
|
nh_t nodeparh;
|
|
nrh_t nrh;
|
|
nh_t grandcldh;
|
|
nh_t nextcldh;
|
|
bool_t ok;
|
|
|
|
cldp = Node_map( cldh );
|
|
ino = cldp->n_ino;
|
|
gen = cldp->n_gen;
|
|
nodeparh = cldp->n_parh;
|
|
nrh = cldp->n_nrh;
|
|
grandcldh = cldp->n_cldh;
|
|
nextcldh = cldp->n_sibh;
|
|
Node_unmap( cldh, &cldp );
|
|
|
|
if ( parh == persp->p_orphh ) {
|
|
sprintf( tranp->t_namebuf, "%llu.%u", ino, gen );
|
|
} else if ( cldh == persp->p_orphh ) {
|
|
sprintf( tranp->t_namebuf, "%llu.%u", ino, gen );
|
|
} else {
|
|
intgen_t namelen;
|
|
namelen = namreg_get( nrh,
|
|
tranp->t_namebuf,
|
|
sizeof( tranp->t_namebuf ));
|
|
assert( namelen >= 0 );
|
|
}
|
|
|
|
if ( nodeparh == NH_NULL ) {
|
|
mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_TREE,
|
|
"node %x %s %llu %u parent NULL\n",
|
|
cldh,
|
|
tranp->t_namebuf,
|
|
ino,
|
|
gen );
|
|
return BOOL_FALSE;
|
|
} else if ( nodeparh != parh ) {
|
|
mlog( MLOG_NORMAL | MLOG_WARNING | MLOG_TREE,
|
|
"node %x %s %llu %u parent mismatch: "
|
|
"nodepar %x par %x\n",
|
|
cldh,
|
|
tranp->t_namebuf,
|
|
ino,
|
|
gen,
|
|
nodeparh,
|
|
parh );
|
|
return BOOL_FALSE;
|
|
} else {
|
|
mlog( MLOG_DEBUG | MLOG_TREE,
|
|
"node %x %s %llu %u parent %x\n",
|
|
cldh,
|
|
tranp->t_namebuf,
|
|
ino,
|
|
gen,
|
|
parh );
|
|
}
|
|
ok = tree_chk2_recurse( grandcldh, cldh );
|
|
if ( ! ok ) {
|
|
okaccum = BOOL_FALSE;
|
|
}
|
|
|
|
cldh = nextcldh;
|
|
}
|
|
|
|
return okaccum;
|
|
}
|
|
|
|
#endif /* TREE_CHK */
|
|
|
|
#ifdef WHITEPARSE
|
|
|
|
static char *whites = " \t\r\n\v\f";
|
|
|
|
static int is_white( char c );
|
|
static void fix_escape( char *string, char *liter );
|
|
static void fix_quoted_span( char *string, char *liter );
|
|
static void collapse_white( char *string, char *liter );
|
|
static size_t distance_to_space( char *s, char *l );
|
|
|
|
static int
|
|
parse( int slotcnt, char **slotbuf, char *string )
|
|
{
|
|
char *liter;
|
|
char *s;
|
|
char *l;
|
|
size_t wordcnt;
|
|
|
|
/* sanity checkcs
|
|
*/
|
|
assert( slotcnt >= 0 );
|
|
|
|
/* allocate a companion to the input string for identifying
|
|
* characters which are to be interpreted literally.
|
|
*/
|
|
liter = ( char * )calloc( 1, strlen( string ) + 1 );
|
|
if ( ! liter ) {
|
|
return -1;
|
|
}
|
|
|
|
/* pass 1: collapse escape sequences, identifying characters which
|
|
* are to be interpreted literally
|
|
*/
|
|
for ( s = string, l = liter ; *s ; s++, l++ ) {
|
|
if ( *s == '\\' && ! *l ) {
|
|
fix_escape( s, l );
|
|
}
|
|
}
|
|
|
|
/* pass 2: collapse quoted spans, identifying characters which
|
|
* are to be interpreted literally
|
|
*/
|
|
for ( s = string, l = liter ; *s ; s++, l++ ) {
|
|
if ( *s == '\"' && ! *l ) {
|
|
fix_quoted_span( s, l );
|
|
}
|
|
}
|
|
|
|
/* pass 3: collapse white space spans into a single space
|
|
*/
|
|
for ( s = string, l = liter ; *s ; s++, l++ ) {
|
|
if ( is_white( *s ) && ! *l ) {
|
|
collapse_white( s, l );
|
|
}
|
|
}
|
|
|
|
/* pass 4: identify and null-terminate words
|
|
*/
|
|
wordcnt = 0;
|
|
s = string;
|
|
l = liter;
|
|
while ( *s ) {
|
|
size_t d;
|
|
if ( wordcnt < ( size_t )slotcnt ) {
|
|
slotbuf[ wordcnt ] = s;
|
|
}
|
|
wordcnt++;
|
|
d = distance_to_space( s, l );
|
|
s += d;
|
|
l += d;
|
|
if ( *s ) {
|
|
*s = 0;
|
|
s++;
|
|
l++;
|
|
}
|
|
}
|
|
|
|
free( ( void * )liter );
|
|
return ( int )wordcnt;
|
|
}
|
|
|
|
struct escape_table {
|
|
char sequence;
|
|
char substitute;
|
|
};
|
|
typedef struct escape_table escape_table_t;
|
|
|
|
static escape_table_t escape_table[ ] = {
|
|
'n', '\n',
|
|
't', '\t',
|
|
'v', '\v',
|
|
'b', '\b',
|
|
'r', '\r',
|
|
'f', '\f',
|
|
'f', '\f',
|
|
'a', '\a',
|
|
'\\', '\\',
|
|
};
|
|
|
|
static void shrink( char *s, size_t cnt );
|
|
static int is_hex( char c );
|
|
static size_t hex_to_size( char c );
|
|
static int is_octal( char c );
|
|
static size_t octal_to_size( char c );
|
|
|
|
static void
|
|
fix_escape( char *string, char *liter )
|
|
{
|
|
escape_table_t *ep;
|
|
escape_table_t *endep;
|
|
|
|
/* first look for special escapes described in table
|
|
*/
|
|
ep = escape_table;
|
|
endep = escape_table + ( sizeof( escape_table )
|
|
/
|
|
sizeof( escape_table[ 0 ] ));
|
|
for ( ; ep < endep ; ep++ ) {
|
|
if ( string[ 1 ] == ep->sequence ) {
|
|
string[ 0 ] = ep->substitute;
|
|
liter[ 0 ] = ( char )1;
|
|
shrink( &string[ 1 ], 1 );
|
|
shrink( &liter[ 1 ], 1 );
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* detect white space escapes
|
|
*/
|
|
if ( is_white( string[ 1 ] )) {
|
|
liter[ 0 ] = ( char )1;
|
|
shrink( &string[ 0 ], 1 );
|
|
shrink( &liter[ 1 ], 1 );
|
|
return;
|
|
}
|
|
|
|
/* detect hex escapes (don't allow null)
|
|
*/
|
|
if ( string[ 1 ] == 'x' ) {
|
|
size_t hexlen;
|
|
size_t accum;
|
|
accum = 0;
|
|
for ( hexlen = 0
|
|
;
|
|
hexlen < 2 && is_hex( string[ 2 + hexlen ] )
|
|
;
|
|
hexlen++ ) {
|
|
accum *= 16;
|
|
accum += hex_to_size( string[ 2 + hexlen ] );
|
|
}
|
|
if ( hexlen && accum ) {
|
|
string[ 0 ] = ( char )accum;
|
|
liter[ 0 ] = ( char )1;
|
|
shrink( &string[ 1 ], 1 + hexlen );
|
|
shrink( &liter[ 1 ], 1 + hexlen );
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* detect octal escapes (don't allow null)
|
|
*/
|
|
if ( is_octal( string[ 1 ] )) {
|
|
size_t octallen;
|
|
size_t accum;
|
|
accum = octal_to_size( string[ 1 ] );
|
|
for ( octallen = 1
|
|
;
|
|
octallen < 3 && is_octal( string[ 1 + octallen ] )
|
|
;
|
|
octallen++ ) {
|
|
accum *= 8;
|
|
accum += octal_to_size( string[ 1 + octallen ] );
|
|
}
|
|
if ( accum ) {
|
|
string[ 0 ] = ( char )accum;
|
|
liter[ 0 ] = ( char )1;
|
|
shrink( &string[ 1 ], octallen );
|
|
shrink( &liter[ 1 ], octallen );
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* didn't match any escape sequences, so assume literal
|
|
*/
|
|
liter[ 0 ] = ( char )1;
|
|
}
|
|
|
|
static void
|
|
fix_quoted_span( char *string, char *liter )
|
|
{
|
|
char *s;
|
|
char *l;
|
|
|
|
/* first cover the leading quote
|
|
*/
|
|
shrink( string, 1 );
|
|
shrink( liter, 1 );
|
|
|
|
/* scan for the next non-literal quote, marking all
|
|
* characters in between as literal
|
|
*/
|
|
for ( s = string, l = liter ; *s && ( *s != '\"' || *l ) ; s++, l++ ) {
|
|
*l = ( char )1;
|
|
}
|
|
|
|
if ( *s ) {
|
|
shrink( s, 1 );
|
|
shrink( l, 1 );
|
|
}
|
|
}
|
|
|
|
static void
|
|
collapse_white( char *string, char *liter )
|
|
{
|
|
char *s;
|
|
char *l;
|
|
size_t cnt;
|
|
|
|
cnt = 0;
|
|
for ( s = string, l = liter ; is_white( *s ) && ! *l ; s++, l++ ) {
|
|
cnt++;
|
|
}
|
|
|
|
string[ 0 ] = ' ';
|
|
|
|
if ( cnt > 1 ) {
|
|
shrink( &string[ 1 ], cnt - 1 );
|
|
shrink( &liter[ 1 ], cnt - 1 );
|
|
}
|
|
}
|
|
|
|
static size_t
|
|
distance_to_space( char *s, char *l )
|
|
{
|
|
size_t cnt;
|
|
|
|
for ( cnt = 0 ; *s && ( ! is_white( *s ) || *l ) ; s++, l++ ) {
|
|
cnt++;
|
|
}
|
|
|
|
return cnt;
|
|
}
|
|
|
|
static void
|
|
shrink( char *s, size_t cnt )
|
|
{
|
|
strcpy( s, s + cnt );
|
|
}
|
|
|
|
static int
|
|
is_white( char c )
|
|
{
|
|
if ( c && strchr( whites, ( int )c )) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int
|
|
is_hex( char c )
|
|
{
|
|
if ( c >= '0' && c <= '9' ) {
|
|
return 1;
|
|
}
|
|
|
|
if ( c >= 'a' && c <= 'f' ) {
|
|
return 1;
|
|
}
|
|
|
|
if ( c >= 'A' && c <= 'F' ) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static size_t
|
|
hex_to_size( char c )
|
|
{
|
|
if ( c >= '0' && c <= '9' ) {
|
|
return ( size_t )( c - '0' );
|
|
}
|
|
|
|
if ( c >= 'a' && c <= 'f' ) {
|
|
return ( size_t )( c - 'a' ) + ( size_t )0xa;
|
|
}
|
|
|
|
if ( c >= 'A' && c <= 'F' ) {
|
|
return ( size_t )( c - 'A' ) + ( size_t )0xa;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
is_octal( char c )
|
|
{
|
|
if ( c >= '0' && c <= '7' ) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static size_t
|
|
octal_to_size( char c )
|
|
{
|
|
if ( c >= '0' && c <= '7' ) {
|
|
return ( size_t )( c - '0' );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* WHITEPARSE */
|
|
|
|
static int
|
|
mkdir_r(char *path)
|
|
{
|
|
struct stat sbuf;
|
|
|
|
if (stat(path, &sbuf) < 0) {
|
|
if (mkdir_r(dirname(strdup(path))) < 0)
|
|
return -1;
|
|
return mkdir(path, 0755);
|
|
}
|
|
else if ((sbuf.st_mode & S_IFDIR) == 0) {
|
|
mlog( MLOG_TRACE | MLOG_ERROR | MLOG_TREE,
|
|
"%s is not a directory\n", path);
|
|
exit(1);
|
|
}
|
|
return 0;
|
|
}
|
|
|