示例#1
0
/*
 * timecmp
 *
 * This callback function is used during backup.  It checks
 * if the object specified by the 'attr' should be backed
 * up or not.
 *
 * Directories are backed up anyways for dump format.
 * If this function is called, then the directory is
 * marked in the bitmap vector, it shows that either the
 * directory itself is modified or there is something below
 * it that will be backed up.
 *
 * Directories for tar format are backed up if and only if
 * they are modified.
 *
 * By setting ndmp_force_bk_dirs global variable to a non-zero
 * value, directories are backed up anyways.
 *
 * Backing up the directories unconditionally, helps
 * restoring the metadata of directories as well, when one
 * of the objects below them are being restored.
 *
 * For non-directory objects, if the modification or change
 * time of the object is after the date specified by the
 * bk_selector_t, the the object must be backed up.
 *
 */
static boolean_t
timecmp(bk_selector_t *bksp,
		struct stat64 *attr)
{
	ndmp_lbr_params_t *nlp;

	nlp = (ndmp_lbr_params_t *)bksp->bs_cookie;
	if (S_ISDIR(attr->st_mode) && ndmp_force_bk_dirs) {
		NDMP_LOG(LOG_DEBUG, "d(%lu)",
		    (uint_t)attr->st_ino);
		return (TRUE);
	}
	if (S_ISDIR(attr->st_mode) &&
	    dbm_getone(nlp->nlp_bkmap, (u_longlong_t)attr->st_ino) &&
	    ((NLP_ISDUMP(nlp) && ndmp_dump_path_node) ||
	    (NLP_ISTAR(nlp) && ndmp_tar_path_node))) {
		/*
		 * If the object is a directory and it leads to a modified
		 * object (that should be backed up) and for that type of
		 * backup the path nodes should be backed up, then return
		 * TRUE.
		 *
		 * This is required by some DMAs like Backup Express, which
		 * needs to receive ADD_NODE (for dump) or ADD_PATH (for tar)
		 * for the intermediate directories of a modified object.
		 * Other DMAs, like net_backup and net_worker, do not have such
		 * requirement.  This requirement makes sense for dump format
		 * but for 'tar' format, it does not.  In provision to the
		 * NDMP-v4 spec, for 'tar' format the intermediate directories
		 * need not to be reported.
		 */
		NDMP_LOG(LOG_DEBUG, "p(%lu)", (u_longlong_t)attr->st_ino);
		return (TRUE);
	}
	if (attr->st_mtime > bksp->bs_ldate) {
		NDMP_LOG(LOG_DEBUG, "m(%lu): %lu > %lu",
		    (uint_t)attr->st_ino, (uint_t)attr->st_mtime,
		    (uint_t)bksp->bs_ldate);
		return (TRUE);
	}
	if (attr->st_ctime > bksp->bs_ldate) {
		if (NLP_IGNCTIME(nlp)) {
			NDMP_LOG(LOG_DEBUG, "ign c(%lu): %lu > %lu",
			    (uint_t)attr->st_ino, (uint_t)attr->st_ctime,
			    (uint_t)bksp->bs_ldate);
			return (FALSE);
		}
		NDMP_LOG(LOG_DEBUG, "c(%lu): %lu > %lu",
		    (uint_t)attr->st_ino, (uint_t)attr->st_ctime,
		    (uint_t)bksp->bs_ldate);
		return (TRUE);
	}
	NDMP_LOG(LOG_DEBUG, "mc(%lu): (%lu,%lu) < %lu",
	    (uint_t)attr->st_ino, (uint_t)attr->st_mtime,
	    (uint_t)attr->st_ctime, (uint_t)bksp->bs_ldate);
	return (FALSE);
}
示例#2
0
/*
 * count_bits_cb
 *
 * Call back for counting the set bits in the dbitmap.
 *
 * Parameters:
 *   bmd (input) - bitmap descriptor
 *   bn (input) - the bit number
 *   arg (input) - pointer to the argument
 *
 * Returns:
 *   0: always
 */
static int
count_bits_cb(int bmd, u_longlong_t bn, void *arg)
{
    if (dbm_getone(bmd, bn)) {
        (*(u_longlong_t *)arg)++;
        if (ndmpd_print_inodes)
            syslog(LOG_DEBUG, "%llu", bn);
    }

    return (0);
}
示例#3
0
/*
 * ndmpd_fhdir_v3_cb
 *
 * Callback function for file history dir information
 */
int
ndmpd_fhdir_v3_cb(lbr_fhlog_call_backs_t *cbp, char *dir, struct stat64 *stp)
{
	char nm[PATH_MAX+1];
	int nml;
	int err;
	ulong_t ino, pino;
	ulong_t pos;
	ndmp_lbr_params_t *nlp;
	ndmpd_module_params_t *params;
	DIR *dirp;
	char dirpath[PATH_MAX];

	if (!cbp) {
		err = -1;
		syslog(LOG_DEBUG, "cbp is NULL");
	} else if (!cbp->fh_cookie) {
		err = -1;
		syslog(LOG_DEBUG, "cookie is NULL");
	} else if (!dir) {
		err = -1;
		syslog(LOG_DEBUG, "dir is NULL");
	} else if (!(nlp = ndmp_get_nlp(cbp->fh_cookie))) {
		err = -1;
		syslog(LOG_DEBUG, "nlp is NULL");
	} else
		err = 0;

	if (err != 0)
		return (0);

	if (!NLP_ISSET(nlp, NLPF_FH))
		return (0);

	/*
	 * Veritas net_backup accepts only 2 as the inode number of the backup
	 * root directory.  The other way compares the path against the
	 * backup path which is slower.
	 */
	if (stp->st_ino == nlp->nlp_bkdirino)
		pino = ROOT_INODE;
	else
		pino = stp->st_ino;

	/*
	 * There is nothing below this directory to be backed up.
	 * If there was, the bit for this directory would have
	 * been set.  Backup root directory is exception.  We
	 * always send the dir file history records of it.
	 */
	if (pino != ROOT_INODE &&
	    !dbm_getone(nlp->nlp_bkmap, (u_longlong_t)stp->st_ino)) {
		syslog(LOG_DEBUG, "nothing below here");
		return (0);
	}

	params = nlp->nlp_params;
	if (!params || !params->mp_file_history_dir_func)
		return (-1);

	pos = 0;
	err = 0;

	dirp = opendir(dir);
	if (dirp == NULL)
		return (0);

	do {
		nml = PATH_MAX;
		err = dp_readdir(dirp, &pos, nm, &nml, &ino);
		if (err != 0) {
			syslog(LOG_DEBUG,
			    "%d reading pos %u dir \"%s\"", err, pos, dir);
			break;
		}
		if (nml == 0)
			break;
		nm[nml] = '\0';

		if (pino == ROOT_INODE) {
			if (rootfs_dot_or_dotdot(nm))
				ino = ROOT_INODE;
		} else if (ino == nlp->nlp_bkdirino && IS_DOTDOT(nm)) {
			ino = ROOT_INODE;
		}

		if (!dbm_getone(nlp->nlp_bkmap, (u_longlong_t)ino))
			continue;

		/*
		 * If the entry is on exclusion list dont send the info
		 */
		if (tlm_is_excluded(dir, nm, ndmp_excl_list)) {
			syslog(LOG_DEBUG,
			    "name \"%s\" skipped", nm == 0 ? "nil" : nm);
			continue;
		}

		err = (*params->mp_file_history_dir_func)(cbp->fh_cookie, nm,
		    ino, pino);
		if (err < 0) {
			syslog(LOG_ERR, "\"%s\": %d", dir, err);
			break;
		}

		/*
		 * This is a requirement by some DMA's (net_vault) that during
		 * the incremental backup, the node info should also be sent
		 * along with the dir info for all directories leading to a
		 * backed up file.
		 */
		if (ndmp_fhinode) {
			struct stat64 ret_attr;

			(void) strlcpy(dirpath, dir, PATH_MAX);
			(void) strlcat(dirpath, "/", PATH_MAX);
			(void) strlcat(dirpath, nm, PATH_MAX);
			err = stat64(dirpath, &ret_attr);
			if (err != 0) {
				syslog(LOG_ERR,
				    "Error looking up %s", nm);
				break;
			}

			if (S_ISDIR(ret_attr.st_mode)) {
				err = (*params->mp_file_history_node_func)(cbp->
				    fh_cookie, ino, &ret_attr, 0);
				if (err < 0) {
					syslog(LOG_ERR, "\"%s/\": %d",
					    dir, err);
					break;
				}
			}
		}
	} while (err == 0);

	(void) closedir(dirp);
	return (err);
}
示例#4
0
/*
 * backup_work
 *
 * Start the NDMP backup (V2 only).
 */
int
backup_work(char *bk_path, tlm_job_stats_t *job_stats,
    ndmp_run_args_t *np, tlm_commands_t *commands,
    ndmp_lbr_params_t *nlp)
{
	struct full_dir_info dir_info; /* the blob to push/pop with cstack_t */
	struct full_dir_info *t_dir_info, *p_dir_info;
	struct stat64 ret_attr; /* attributes of current file name */
	fs_fhandle_t ret_fh;
	char *first_name; /* where the first name is located */
	char *dname;
	int erc;
	int retval;
	cstack_t *stk;
	unsigned long fileid;
	tlm_acls_t tlm_acls;
	int dname_size;
	longlong_t fsize;
	bk_selector_t bks;
	tlm_cmd_t *local_commands;
	long 	dpos;

	NDMP_LOG(LOG_DEBUG, "nr_chkpnted %d nr_ldate: %u bk_path: \"%s\"",
	    NLP_ISCHKPNTED(nlp), nlp->nlp_ldate, bk_path);

	/* Get every name in this directory */
	dname = ndmp_malloc(TLM_MAX_PATH_NAME);
	if (dname == NULL)
		return (-ENOMEM);

	local_commands = commands->tcs_command;
	retval = 0;
	(void) memset(&bks, 0, sizeof (bks));
	bks.bs_cookie = (void *)nlp;
	bks.bs_level = nlp->nlp_clevel;
	bks.bs_ldate = nlp->nlp_ldate;
	bks.bs_fn = timecmp;

	/*
	 * should we skip the whole thing?
	 */
	if (tlm_is_excluded("", bk_path, np->nr_excls)) {
		NDMP_LOG(LOG_DEBUG, "%s excluded", bk_path);
		free(dname);
		return (0);
	}

	/*
	 * Search for the top-level file-directory
	 */
	if (NLP_ISCHKPNTED(nlp)) {
		first_name = np->nr_chkp_nm;
		(void) strlcpy(first_name, bk_path, TLM_MAX_PATH_NAME);
	} else {
		first_name = tlm_build_snapshot_name(bk_path, np->nr_chkp_nm,
		    nlp->nlp_jstat->js_job_name);
	}

	(void) memset(&ret_fh, 0, sizeof (ret_fh));
	erc = fs_getstat(first_name, &ret_fh, &ret_attr);
	if (erc != 0) {
		NDMP_LOG(LOG_ERR, "Path %s not found.", first_name);
		free(dname);
		return (-EINVAL);
	}

	if ((stk = cstack_new()) == NULL) {
		free(dname);
		NDMP_LOG(LOG_DEBUG, "cstack_new failed");
		return (-ENOMEM);
	}
	(void) strlcpy(dir_info.fd_dir_name, first_name, TLM_MAX_PATH_NAME);
	(void) memcpy(&dir_info.fd_dir_fh, &ret_fh, sizeof (fs_fhandle_t));
	p_dir_info = dup_dir_info(&dir_info);

	/*
	 * Push the first name onto the stack so that we can pop it back
	 * off as part of the normal cycle
	 */
	if (cstack_push(stk, p_dir_info, 0)) {
		free(dname);
		free(p_dir_info);
		cstack_delete(stk);
		NDMP_LOG(LOG_DEBUG, "cstack_push failed");
		return (-ENOMEM);
	}

	(void) memset(&tlm_acls, 0, sizeof (tlm_acls));
	/*
	 * Did NDMP create a checkpoint?
	 */
	if (NLP_ISCHKPNTED(nlp) || fs_is_rdonly(bk_path)) {
		tlm_acls.acl_checkpointed = FALSE;
	} else {
		/* Use the checkpoint created by NDMP */
		tlm_acls.acl_checkpointed = TRUE;
	}

	/*
	 * This is level-backup.  It never resets the archive bit.
	 */
	tlm_acls.acl_clear_archive = FALSE;

	NDMP_LOG(LOG_DEBUG, "acls.chkpnt: %c acls.clear_arcbit: %c",
	    NDMP_YORN(tlm_acls.acl_checkpointed),
	    NDMP_YORN(tlm_acls.acl_clear_archive));

	while (commands->tcs_reader == TLM_BACKUP_RUN &&
	    local_commands->tc_reader == TLM_BACKUP_RUN &&
	    cstack_pop(stk, (void **)&p_dir_info, 0) == 0) {

		if (NLP_ISCHKPNTED(nlp))
			(void) strlcpy(np->nr_unchkp_nm,
			    p_dir_info->fd_dir_name, TLM_MAX_PATH_NAME);
		else
			(void) tlm_remove_checkpoint(p_dir_info->fd_dir_name,
			    np->nr_unchkp_nm);

		(void) backup_dir(np->nr_unchkp_nm, &tlm_acls, local_commands,
		    job_stats, &bks);


		while (commands->tcs_reader == TLM_BACKUP_RUN &&
		    local_commands->tc_reader == TLM_BACKUP_RUN) {

			dname_size = TLM_MAX_PATH_NAME - 1;

			NDMP_LOG(LOG_DEBUG,
			    "dir_name: %s", p_dir_info->fd_dir_name);

			(void) memset(&ret_fh, 0, sizeof (ret_fh));
			erc = fs_readdir(&p_dir_info->fd_dir_fh,
			    p_dir_info->fd_dir_name, &dpos,
			    dname, &dname_size, &ret_fh, &ret_attr);
			if (erc == 0) {
				fileid = ret_fh.fh_fid;
			} else {
				NDMP_LOG(LOG_DEBUG,
				    "Filesystem readdir in [%s]",
				    p_dir_info->fd_dir_name);
				retval = -ENOENT;
				break;
			}

			/* an empty name size marks the end of the list */
			if (dname_size == 0)
				break;
			dname[dname_size] = '\0';

			NDMP_LOG(LOG_DEBUG, "dname: \"%s\"", dname);

			/*
			 * If name refers to a directory, push its file
			 *   handle onto the stack  (skip "." and "..").
			 */
			if (rootfs_dot_or_dotdot(dname)) {
				fileid = 0;
				continue;
			}

			/*
			 * Skip the:
			 * non-dir entries which should not be backed up
			 * Or
			 * dir-type entries which have have nothing under
			 * their hierarchy to be backed up.
			 */
			if (!dbm_getone(nlp->nlp_bkmap, (u_longlong_t)fileid)) {
				NDMP_LOG(LOG_DEBUG, "Skipping %s/%s",
				    p_dir_info->fd_dir_name, dname);
				fileid = 0;
				continue;
			}

			if (tlm_is_excluded(np->nr_unchkp_nm, dname,
			    np->nr_excls)) {
				fileid = 0;
				continue;
			}
			if (S_ISDIR(ret_attr.st_mode)) {
				/*
				 * only directories get pushed onto this stack,
				 * so we do not have to test for regular files.
				 */
				t_dir_info = tlm_new_dir_info(&ret_fh,
				    p_dir_info->fd_dir_name, dname);
				if (t_dir_info == NULL) {
					NDMP_LOG(LOG_DEBUG,
					    "While backing up [%s][%s]",
					    p_dir_info->fd_dir_name, dname);
				} else if (cstack_push(stk, t_dir_info,
				    0) != 0) {
					NDMP_LOG(LOG_DEBUG,
					    "No enough memory stack_push");
					retval = -ENOMEM;
					break;
				}
			} else if (S_ISREG(ret_attr.st_mode) ||
			    S_ISLNK(ret_attr.st_mode)) {

				fsize = backup_file(np->nr_unchkp_nm, dname,
				    &tlm_acls, commands, local_commands,
				    job_stats, &bks);

				if (fsize >= 0) {
					job_stats->js_files_so_far++;
					job_stats->js_bytes_total += fsize;
				} else
					job_stats->js_errors++;
				fileid = 0;
			}
		}
		fileid = 0;
		free(p_dir_info);
		if (retval != 0)
			break;
	}

	free(dname);

	while (cstack_pop(stk, (void **)&p_dir_info, 0) == 0) {
		free(p_dir_info);
	}

	cstack_delete(stk);
	return (retval);
}
示例#5
0
/*
 * marklbrv3_cb
 *
 * The callback function, called by traverse_post to mark
 * bits in the bitmap.
 *
 * It's so much like mark_cb for time-based (token-based
 * and level-type) backup types, except that it looks at
 * the archive bit of the objects instead of their timestamp.
 *
 * Parameters:
 *   arg (input) - pointer to the mark parameter
 *   pnp (input) - pointer to the path node
 *   enp (input) - pointer to the entry node
 *
 * Returns:
 *   0: as long as traversing should continue
 *   != 0: if traversing should stop
 */
int
marklbrv3_cb(void *arg, fst_node_t *pnp, fst_node_t *enp)
{
    int bmd;
    u_longlong_t bl;
    fs_fhandle_t *pfhp, *efhp;
    struct stat64 *pstp, *estp;
    mark_param_t *mpp;
    ndmp_lbr_params_t *nlp;

    mpp = (mark_param_t *)arg;
    if (!mpp) {
        syslog(LOG_ERR, "NULL argument passed in marklbrv3");
        return (-1);
    }
    nlp = ndmp_get_nlp(mpp->mp_session);
    if (mpp->mp_session->ns_data.dd_abort ||
            (nlp && NLP_ISSET(nlp, NLPF_ABORTED))) {
        syslog(LOG_ERR, "Processing directories aborted.");
        return (-1);
    }

    bmd = mpp->mp_bmd;
    bl = dbm_getlen(bmd);

    pfhp = pnp->tn_fh;
    pstp = pnp->tn_st;

    /* sanity check on fh and stat of the path passed */
    if (pstp->st_ino > bl) {
        syslog(LOG_ERR, "Invalid path inode #%u",
               (uint_t)pstp->st_ino);
        return (-1);
    }
    if (pstp->st_ino != pfhp->fh_fid) {
        syslog(LOG_ERR, "Path ino mismatch %u %u",
               (uint_t)pstp->st_ino, (uint_t)pfhp->fh_fid);
        return (-1);
    }

    /*
     * Always mark the backup path inode number.
     */
    if (!enp->tn_path) {
        (void) dbm_setone(bmd, pstp->st_ino);
        return (0);
    }

    efhp = enp->tn_fh;
    estp = enp->tn_st;

    /* sanity check on fh and stat of the entry passed */
    if (estp->st_ino > bl) {
        syslog(LOG_DEBUG, "Invalid entry inode #%u",
               (uint_t)estp->st_ino);
        return (-1);
    }
    if (estp->st_ino != efhp->fh_fid) {
        syslog(LOG_DEBUG, "Entry ino mismatch %u %u", estp->st_ino,
               (uint_t)pfhp->fh_fid);
        return (-1);
    }

    if (S_ISDIR(estp->st_mode) &&
            dbm_getone(bmd, (u_longlong_t)estp->st_ino)) {
        (void) dbm_setone(bmd, (u_longlong_t)pstp->st_ino);
    }

    return (0);
}
示例#6
0
/*
 * mark_cb
 *
 * The callback function, called by traverse_post to mark bits
 * in the bitmap.
 *
 * Set the bit of the entry if it's been modified (obviously
 * should be backed up) plus its parent directory.
 *
 * If the entry is a directory and is not modified itself,
 * but it's marked, then there is something below it that
 * is being backed up.  It shows the the path, leads to
 * an object that will be backed up. So the path should
 * be marked too.
 *
 * The backup path itself is always marked.
 *
 * Parameters:
 *   arg (input) - pointer to the mark parameter
 *   pnp (input) - pointer to the path node
 *   enp (input) - pointer to the entry node
 *
 * Returns:
 *   0: as long as traversing should continue
 *   != 0: if traversing should stop
 */
int
mark_cb(void *arg, fst_node_t *pnp, fst_node_t *enp)
{
    int bmd;
    int rv;
    u_longlong_t bl;
    time_t ddate;
    fs_fhandle_t *pfhp, *efhp;
    struct stat64 *pstp, *estp;
    mark_param_t *mpp;
    ndmp_lbr_params_t *nlp;
    tlm_acls_t *tacl;

    rv = 0;
    mpp = (mark_param_t *)arg;
    tacl = mpp->mp_tacl;
    nlp = ndmp_get_nlp(mpp->mp_session);
    if (!mpp) {
        syslog(LOG_ERR, "NULL argument passed");
        rv = -1;
    } else if (mpp->mp_session->ns_eof) {
        syslog(LOG_INFO, "Connection to the client is closed");
        rv = -1;
    } else if (mpp->mp_session->ns_data.dd_abort ||
               (nlp && NLP_ISSET(nlp, NLPF_ABORTED))) {
        syslog(LOG_INFO, "Processing directories aborted.");
        rv = -1;
    }

    if (rv != 0)
        return (rv);

    ddate = mpp->mp_ddate;
    bmd = mpp->mp_bmd;
    bl = dbm_getlen(bmd);

    pfhp = pnp->tn_fh;
    pstp = pnp->tn_st;

    /* sanity check on fh and stat of the path passed */
    if (pstp->st_ino > bl) {
        syslog(LOG_ERR, "Invalid path inode #%u",
               (uint_t)pstp->st_ino);
        return (-1);
    }
    if (pstp->st_ino != pfhp->fh_fid) {
        syslog(LOG_ERR, "Path ino mismatch %u %u",
               (uint_t)pstp->st_ino, (uint_t)pfhp->fh_fid);
        return (-1);
    }

    /*
     * Always mark the backup path inode number.
     */
    if (!enp->tn_path) {
        (void) dbm_setone(bmd, pstp->st_ino);
        return (0);
    }

    efhp = enp->tn_fh;
    estp = enp->tn_st;

    /* sanity check on fh and stat of the entry passed */
    if (estp->st_ino > bl) {
        syslog(LOG_ERR, "Invalid entry inode #%u",
               (uint_t)estp->st_ino);
        return (-1);
    }
    if (estp->st_ino != efhp->fh_fid) {
        syslog(LOG_ERR, "Entry ino mismatch %u %u", estp->st_ino,
               (uint_t)pfhp->fh_fid);
        return (-1);
    }

    /* check the dates and mark the bitmap inode */
    if (ddate == 0) {
        /* base backup */
        (void) dbm_setone(bmd, (u_longlong_t)estp->st_ino);
        (void) dbm_setone(bmd, (u_longlong_t)pstp->st_ino);

    } else if (estp->st_mtime > ddate) {
        (void) dbm_setone(bmd, (u_longlong_t)estp->st_ino);
        (void) dbm_setone(bmd, (u_longlong_t)pstp->st_ino);
        if (ndmpd_verbose_traverse) {
            syslog(LOG_DEBUG,
                   "m(%u,%u,%u,%u)", (uint_t)pstp->st_ino,
                   (uint_t)estp->st_ino, (uint_t)estp->st_mtime,
                   (uint_t)ddate);
            syslog(LOG_DEBUG, "\"%s/%s\"",
                   pnp->tn_path, enp->tn_path);
        }
    } else if (iscreated(nlp, NULL, tacl, ddate)) {
        (void) dbm_setone(bmd, (u_longlong_t)estp->st_ino);
        (void) dbm_setone(bmd, (u_longlong_t)pstp->st_ino);
        if (ndmpd_verbose_traverse) {
            syslog(LOG_DEBUG,
                   "cr(%u,%u,%u,%u)", (uint_t)pstp->st_ino,
                   (uint_t)estp->st_ino, (uint_t)estp->st_mtime,
                   (uint_t)ddate);
            syslog(LOG_DEBUG, "\"%s/%s\"",
                   pnp->tn_path, enp->tn_path);
        }
    } else if (estp->st_ctime > ddate) {
        if (!NLP_IGNCTIME(nlp)) {
            (void) dbm_setone(bmd, (u_longlong_t)estp->st_ino);
            (void) dbm_setone(bmd, (u_longlong_t)pstp->st_ino);
        }
        if (ndmpd_verbose_traverse) {
            if (NLP_IGNCTIME(nlp)) {
                syslog(LOG_DEBUG,
                       "ign c(%u,%u,%u,%u)", (uint_t)pstp->st_ino,
                       (uint_t)estp->st_ino,
                       (uint_t)estp->st_ctime, (uint_t)ddate);
            } else {
                syslog(LOG_DEBUG,
                       "c(%u,%u,%u,%u)", (uint_t)pstp->st_ino,
                       (uint_t)estp->st_ino,
                       (uint_t)estp->st_ctime, (uint_t)ddate);
            }
            syslog(LOG_DEBUG, "\"%s/%s\"",
                   pnp->tn_path, enp->tn_path);
        }
    } else if (S_ISDIR(estp->st_mode) &&
               dbm_getone(bmd, (u_longlong_t)estp->st_ino)) {
        (void) dbm_setone(bmd, (u_longlong_t)pstp->st_ino);
    }

    return (0);
}