/* * Get directory entries info and return in the buffer. Cookie * will keep the state of each call */ static int fs_getdents(int fildes, struct dirent *buf, size_t *nbyte, char *pn_path, long *dpos, longlong_t *cookie, long *n_entries, dent_arg_t *darg) { struct dirent *ptr; char file_path[PATH_MAX + 1]; fs_fhandle_t fh; struct stat64 st; long basep; char *p; int len; int rv; if (*nbyte == 0) { (void) memset((char *)buf, 0, MAX_DENT_BUF_SIZE); *nbyte = rv = getdirentries(fildes, (char *)buf, darg->da_size, &basep); *cookie = 0LL; if (rv <= 0) return (rv); } p = (char *)buf + *cookie; len = *nbyte; do { /* LINTED improper alignment */ ptr = (struct dirent *)p; *dpos = basep; if (rootfs_dot_or_dotdot(ptr->d_name)) goto skip_entry; (void) snprintf(file_path, PATH_MAX, "%s/", pn_path); (void) strlcat(file_path, ptr->d_name, PATH_MAX + 1); (void) memset(&fh, 0, sizeof (fs_fhandle_t)); if (lstat(file_path, &st) != 0) { rv = -1; break; } fh.fh_fid = st.st_ino; if (S_ISDIR(st.st_mode)) goto skip_entry; if (fs_populate_dents(darg, strlen(ptr->d_name), (char *)ptr->d_name, n_entries, &st, &fh) != 0) break; skip_entry: p = p + ptr->d_reclen; len -= ptr->d_reclen; } while (len); *cookie = (longlong_t)(p - (char *)buf); *nbyte = len; return (rv); }
/* * 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); }
/* * 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); }
/* * Traverse the file system in the level-order way. The description * and example is in the header file. */ int traverse_level(struct fs_traverse *ftp) { char path[PATH_MAX + 1]; /* full path name of the current dir */ char nm[NAME_MAX + 1]; /* directory entry name */ char *lp; /* last position on the path */ int next_dir, rv; int pl, el; /* path and directory entry length */ cstack_t *sp; fs_fhandle_t pfh, efh; struct stat64 pst, est; traverse_state_t *tsp; struct fst_node pn, en; /* parent and entry nodes */ dent_arg_t darg; if (!ftp || !ftp->ft_path || !*ftp->ft_path || !ftp->ft_callbk) { NDMP_LOG(LOG_DEBUG, "Invalid argument"); errno = EINVAL; return (-1); } /* set the default log function if it's not already set */ if (!ftp->ft_logfp) { ftp->ft_logfp = (ft_log_t)syslog; NDMP_LOG(LOG_DEBUG, "Log to system log \"%s\"", ftp->ft_path); } if (!ftp->ft_lpath) { NDMP_LOG(LOG_DEBUG, "report the same paths \"%s\"", ftp->ft_path); ftp->ft_lpath = ftp->ft_path; } pl = strlen(ftp->ft_lpath); if (pl + 1 > PATH_MAX) { /* +1 for the '/' */ NDMP_LOG(LOG_DEBUG, "lpath too long \"%s\"", ftp->ft_path); errno = ENAMETOOLONG; return (-1); } (void) strcpy(path, ftp->ft_lpath); (void) memset(&pfh, 0, sizeof (pfh)); rv = fs_getstat(ftp->ft_lpath, &pfh, &pst); if (rv != 0) { NDMP_LOG(LOG_DEBUG, "Error %d on fs_getstat(%s)", rv, ftp->ft_path); return (-1); } en.tn_path = NULL; en.tn_fh = NULL; en.tn_st = NULL; if (!S_ISDIR(pst.st_mode)) { pn.tn_path = ftp->ft_lpath; pn.tn_fh = &pfh; pn.tn_st = &pst; rv = CALLBACK(&pn, &en); if (VERBOSE(ftp)) NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv); free(pfh.fh_fpath); return (rv); } sp = cstack_new(); if (!sp) { free(pfh.fh_fpath); errno = ENOMEM; return (-1); } tsp = new_tsp(path); if (!tsp) { cstack_delete(sp); free(pfh.fh_fpath); errno = ENOMEM; return (-1); } darg.da_buf = ndmp_malloc(MAX_DENT_BUF_SIZE); if (!darg.da_buf) { cstack_delete(sp); free(pfh.fh_fpath); free(tsp); errno = ENOMEM; return (-1); } darg.da_size = MAX_DENT_BUF_SIZE; tsp->ts_ent = tsp->ts_end; tsp->ts_fh = pfh; tsp->ts_st = pst; pn.tn_path = path; pn.tn_fh = &tsp->ts_fh; pn.tn_st = &tsp->ts_st; /* call the callback function on the path itself */ traverse_stats.fss_dir_calls++; rv = CALLBACK(&pn, &en); if (rv < 0) { free(tsp); goto end; } if (rv == FST_SKIP) { traverse_stats.fss_dir_skipped++; free(tsp); rv = 0; goto end; } rv = 0; next_dir = 1; do { if (next_dir) { traverse_stats.fss_newdirs++; *tsp->ts_end = '\0'; if (VERBOSE(ftp)) NDMP_LOG(LOG_DEBUG, "pl %d \"%s\"", pl, path); rv = traverse_level_nondir(ftp, tsp, &pn, &darg); if (rv < 0) { NEGATE(rv); free(tsp->ts_fh.fh_fpath); free(tsp); break; } /* * If skipped by the callback function or * error happened reading the information */ if (rv == FST_SKIP || rv == SKIP_ENTRY) { /* * N.B. next_dir should be set to 0 as * well. This prevents the infinite loop. * If it's not set the same directory will * be poped from the stack and will be * scanned again. */ next_dir = 0; rv = 0; goto skip_dir; } /* re-start reading entries of the directory */ tsp->ts_dpos = 0; } next_dir = 0; do { el = NAME_MAX; rv = fs_readdir(&tsp->ts_fh, pn.tn_path, &tsp->ts_dpos, nm, &el, &efh, &est); if (rv != 0) { traverse_stats.fss_readdir_err++; NDMP_LOG(LOG_DEBUG, "Error %d on readdir(%s) pos %d", rv, path, tsp->ts_dpos); if (STOP_ONERR(ftp)) break; rv = SKIP_ENTRY; continue; } /* done with this directory */ if (el == 0) break; nm[el] = '\0'; if (rootfs_dot_or_dotdot(nm)) { free(efh.fh_fpath); continue; } if (VERBOSE(ftp)) NDMP_LOG(LOG_DEBUG, "%u dname: \"%s\"", tsp->ts_dpos, nm); if (pl + 1 + el > PATH_MAX) { /* * The long paths were already encountered * when processing non-dir entries in. * traverse_level_nondir. * We don't increase fss_longpath_err * counter for them again here. */ NDMP_LOG(LOG_ERR, "Path %s/%s is too long.", path, nm); if (STOP_ONLONG(ftp)) rv = ENAMETOOLONG; free(efh.fh_fpath); continue; } if (!S_ISDIR(est.st_mode)) continue; /* * Call the callback function for the new * directory found, then push the current * directory on to the stack. Then dive * into the entry found. */ traverse_stats.fss_dir_calls++; en.tn_path = nm; en.tn_fh = &efh; en.tn_st = &est; rv = CALLBACK(&pn, &en); if (rv < 0) { NEGATE(rv); free(efh.fh_fpath); break; } if (rv == FST_SKIP) { traverse_stats.fss_dir_skipped++; free(efh.fh_fpath); rv = 0; continue; } /* * Push the current directory on to the stack and * dive into the entry found. */ if (cstack_push(sp, tsp, 0)) { rv = ENOMEM; } else { traverse_stats.fss_pushes++; lp = tsp->ts_end; *tsp->ts_end = '/'; (void) strcpy(tsp->ts_end + 1, nm); tsp = new_tsp(path); if (!tsp) rv = ENOMEM; else { next_dir = 1; pl += el + 1; tsp->ts_fh = efh; tsp->ts_st = est; tsp->ts_ent = lp; pn.tn_fh = &tsp->ts_fh; pn.tn_st = &tsp->ts_st; } } break; } while (rv == 0); /* * A new directory must be processed, go to the start of * the loop, open it and process it. */ if (next_dir) continue; skip_dir: if (tsp) { free(tsp->ts_fh.fh_fpath); free(tsp); } if (rv == SKIP_ENTRY) rv = 0; if (rv == 0) { if (cstack_pop(sp, (void **)&tsp, (int *)NULL)) break; traverse_stats.fss_pops++; if (VERBOSE(ftp)) NDMP_LOG(LOG_DEBUG, "Poped pl %d \"%s\"", pl, path); *tsp->ts_end = '\0'; pl = tsp->ts_end - path; pn.tn_fh = &tsp->ts_fh; pn.tn_st = &tsp->ts_st; } } while (rv == 0); /* * Pop and free all the remaining entries on the stack. */ while (!cstack_pop(sp, (void **)&tsp, (int *)NULL)) { traverse_stats.fss_stack_residue++; free(tsp->ts_fh.fh_fpath); free(tsp); } end: free(darg.da_buf); cstack_delete(sp); return (rv); }
/* * Traverse the file system in the post-order way. The description * and example is in the header file. * * The callback function should return 0, on success and non-zero on * failure. If the callback function returns non-zero return value, * the traversing stops. */ int traverse_post(struct fs_traverse *ftp) { char path[PATH_MAX + 1]; /* full path name of the current dir */ char nm[NAME_MAX + 1]; /* directory entry name */ char *lp; /* last position on the path */ int next_dir, rv; int pl, el; /* path and directory entry length */ cstack_t *sp; fs_fhandle_t pfh, efh; struct stat64 pst, est; traverse_state_t *tsp; struct fst_node pn, en; /* parent and entry nodes */ if (!ftp || !ftp->ft_path || !*ftp->ft_path || !ftp->ft_callbk) { NDMP_LOG(LOG_DEBUG, "Invalid argument"); errno = EINVAL; return (-1); } /* set the default log function if it's not already set */ if (!ftp->ft_logfp) { ftp->ft_logfp = (ft_log_t)syslog; NDMP_LOG(LOG_DEBUG, "Log to system log \"%s\"", ftp->ft_path); } /* set the logical path to physical path if it's not already set */ if (!ftp->ft_lpath) { NDMP_LOG(LOG_DEBUG, "report the same paths: \"%s\"", ftp->ft_path); ftp->ft_lpath = ftp->ft_path; } pl = strlen(ftp->ft_lpath); if (pl + 1 > PATH_MAX) { /* +1 for the '/' */ NDMP_LOG(LOG_DEBUG, "lpath too long \"%s\"", ftp->ft_path); errno = ENAMETOOLONG; return (-1); } (void) strcpy(path, ftp->ft_lpath); (void) memset(&pfh, 0, sizeof (pfh)); rv = fs_getstat(ftp->ft_lpath, &pfh, &pst); if (rv != 0) { NDMP_LOG(LOG_DEBUG, "Error %d on fs_getstat(%s)", rv, ftp->ft_path); return (rv); } if (!S_ISDIR(pst.st_mode)) { pn.tn_path = ftp->ft_lpath; pn.tn_fh = &pfh; pn.tn_st = &pst; en.tn_path = NULL; en.tn_fh = NULL; en.tn_st = NULL; rv = CALLBACK(&pn, &en); if (VERBOSE(ftp)) NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv); free(pfh.fh_fpath); return (rv); } sp = cstack_new(); if (!sp) { errno = ENOMEM; free(pfh.fh_fpath); return (-1); } tsp = new_tsp(path); if (!tsp) { cstack_delete(sp); errno = ENOMEM; free(pfh.fh_fpath); return (-1); } tsp->ts_ent = tsp->ts_end; tsp->ts_fh = pfh; tsp->ts_st = pst; pn.tn_path = path; pn.tn_fh = &tsp->ts_fh; pn.tn_st = &tsp->ts_st; rv = 0; next_dir = 1; do { if (next_dir) { traverse_stats.fss_newdirs++; *tsp->ts_end = '\0'; if (VERBOSE(ftp)) NDMP_LOG(LOG_DEBUG, "pl %d \"%s\"", pl, path); } next_dir = 0; do { el = NAME_MAX; rv = fs_readdir(&tsp->ts_fh, pn.tn_path, &tsp->ts_dpos, nm, &el, &efh, &est); if (rv != 0) { free(efh.fh_fpath); traverse_stats.fss_readdir_err++; NDMP_LOG(LOG_DEBUG, "Error %d on readdir(%s) pos %d", rv, path, tsp->ts_dpos); if (STOP_ONERR(ftp)) break; rv = SKIP_ENTRY; continue; } /* done with this directory */ if (el == 0) { if (VERBOSE(ftp)) NDMP_LOG(LOG_DEBUG, "Done(%s)", pn.tn_path); break; } nm[el] = '\0'; if (rootfs_dot_or_dotdot(nm)) { free(efh.fh_fpath); continue; } if (VERBOSE(ftp)) NDMP_LOG(LOG_DEBUG, "%u dname: \"%s\"", tsp->ts_dpos, nm); if (pl + 1 + el > PATH_MAX) { traverse_stats.fss_longpath_err++; NDMP_LOG(LOG_ERR, "Path %s/%s is too long.", path, nm); if (STOP_ONLONG(ftp)) rv = ENAMETOOLONG; free(efh.fh_fpath); continue; } /* * Push the current directory on to the stack and * dive into the entry found. */ if (S_ISDIR(est.st_mode)) { assert(tsp != NULL); if (cstack_push(sp, tsp, 0)) { rv = ENOMEM; free(efh.fh_fpath); break; } traverse_stats.fss_pushes++; /* * Concatenate the current entry with the * current path. This will be the path of * the new directory to be scanned. * * Note: * sprintf(tsp->ts_end, "/%s", de->d_name); * could be used here, but concatenating * strings like this might be faster. * The length of the new path has been * checked above. So strcpy() can be * safe and should not lead to a buffer * over-run. */ lp = tsp->ts_end; *tsp->ts_end = '/'; (void) strcpy(tsp->ts_end + 1, nm); tsp = new_tsp(path); if (!tsp) { free(efh.fh_fpath); rv = ENOMEM; } else { next_dir = 1; pl += el; tsp->ts_fh = efh; tsp->ts_st = est; tsp->ts_ent = lp; pn.tn_fh = &tsp->ts_fh; pn.tn_st = &tsp->ts_st; } break; } else { /* * The entry is not a directory so the * callback function must be called. */ traverse_stats.fss_nondir_calls++; en.tn_path = nm; en.tn_fh = &efh; en.tn_st = &est; rv = CALLBACK(&pn, &en); free(efh.fh_fpath); if (VERBOSE(ftp)) NDMP_LOG(LOG_DEBUG, "CALLBACK(%s/%s): %d", pn.tn_path, en.tn_path, rv); if (rv != 0) break; } } while (rv == 0); /* * A new directory must be processed, go to the start of * the loop, open it and process it. */ if (next_dir) continue; if (rv == SKIP_ENTRY) rv = 0; /* We should skip the current directory */ if (rv == 0) { /* * Remove the ent from the end of path and send it * as an entry of the path. */ lp = tsp->ts_ent; *lp = '\0'; efh = tsp->ts_fh; est = tsp->ts_st; free(tsp); if (cstack_pop(sp, (void **)&tsp, (int *)NULL)) break; assert(tsp != NULL); pl = tsp->ts_end - path; if (VERBOSE(ftp)) NDMP_LOG(LOG_DEBUG, "poped pl %d 0x%p \"%s\"", pl, tsp, path); traverse_stats.fss_pops++; traverse_stats.fss_dir_calls++; pn.tn_fh = &tsp->ts_fh; pn.tn_st = &tsp->ts_st; en.tn_path = lp + 1; en.tn_fh = &efh; en.tn_st = &est; rv = CALLBACK(&pn, &en); free(efh.fh_fpath); if (VERBOSE(ftp)) NDMP_LOG(LOG_DEBUG, "CALLBACK(%s/%s): %d", pn.tn_path, en.tn_path, rv); /* * Does not need to free tsp here. It will be released * later. */ } if (rv != 0 && tsp) { free(tsp->ts_fh.fh_fpath); free(tsp); } } while (rv == 0); /* * For the 'ftp->ft_path' directory itself. */ if (rv == 0) { traverse_stats.fss_dir_calls++; pn.tn_fh = &efh; pn.tn_st = &est; en.tn_path = NULL; en.tn_fh = NULL; en.tn_st = NULL; rv = CALLBACK(&pn, &en); if (VERBOSE(ftp)) NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv); } /* * Pop and free all the remaining entries on the stack. */ while (!cstack_pop(sp, (void **)&tsp, (int *)NULL)) { traverse_stats.fss_stack_residue++; free(tsp->ts_fh.fh_fpath); free(tsp); } cstack_delete(sp); return (rv); }