/* * 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); }
/* * 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); }
/* * 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); }
/* * 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); }
/* * 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); }