/* * Get information of all renames which occurred between 'o_tree' and * 'tree'. We need the three trees in the merge ('o_tree', 'a_tree' and * 'b_tree') to be able to associate the correct cache entries with * the rename information. 'tree' is always equal to either a_tree or b_tree. */ static struct path_list *get_renames(struct tree *tree, struct tree *o_tree, struct tree *a_tree, struct tree *b_tree, struct path_list *entries) { int i; struct path_list *renames; struct diff_options opts; renames = xcalloc(1, sizeof(struct path_list)); diff_setup(&opts); DIFF_OPT_SET(&opts, RECURSIVE); opts.detect_rename = DIFF_DETECT_RENAME; opts.rename_limit = merge_rename_limit >= 0 ? merge_rename_limit : diff_rename_limit >= 0 ? diff_rename_limit : 500; opts.warn_on_too_large_rename = 1; opts.output_format = DIFF_FORMAT_NO_OUTPUT; if (diff_setup_done(&opts) < 0) die("diff setup failed"); diff_tree_sha1(o_tree->object.sha1, tree->object.sha1, "", &opts); diffcore_std(&opts); for (i = 0; i < diff_queued_diff.nr; ++i) { struct path_list_item *item; struct rename *re; struct diff_filepair *pair = diff_queued_diff.queue[i]; if (pair->status != 'R') { diff_free_filepair(pair); continue; } re = xmalloc(sizeof(*re)); re->processed = 0; re->pair = pair; item = path_list_lookup(re->pair->one->path, entries); if (!item) re->src_entry = insert_stage_data(re->pair->one->path, o_tree, a_tree, b_tree, entries); else re->src_entry = item->util; item = path_list_lookup(re->pair->two->path, entries); if (!item) re->dst_entry = insert_stage_data(re->pair->two->path, o_tree, a_tree, b_tree, entries); else re->dst_entry = item->util; item = path_list_insert(pair->one->path, renames); item->util = re; } opts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_queued_diff.nr = 0; diff_flush(&opts); return renames; }
/* * Create a dictionary mapping file names to stage_data objects. The * dictionary contains one entry for every path with a non-zero stage entry. */ static struct path_list *get_unmerged(void) { struct path_list *unmerged = xcalloc(1, sizeof(struct path_list)); int i; unmerged->strdup_paths = 1; for (i = 0; i < active_nr; i++) { struct path_list_item *item; struct stage_data *e; struct cache_entry *ce = active_cache[i]; if (!ce_stage(ce)) continue; item = path_list_lookup(ce->name, unmerged); if (!item) { item = path_list_insert(ce->name, unmerged); item->util = xcalloc(1, sizeof(struct stage_data)); } e = item->util; e->stages[ce_stage(ce)].mode = ce->ce_mode; hashcpy(e->stages[ce_stage(ce)].sha, ce->sha1); } return unmerged; }
static int process_renames(struct path_list *a_renames, struct path_list *b_renames, const char *a_branch, const char *b_branch) { int clean_merge = 1, i, j; struct path_list a_by_dst = {NULL, 0, 0, 0}, b_by_dst = {NULL, 0, 0, 0}; const struct rename *sre; for (i = 0; i < a_renames->nr; i++) { sre = a_renames->items[i].util; path_list_insert(sre->pair->two->path, &a_by_dst)->util = sre->dst_entry; } for (i = 0; i < b_renames->nr; i++) { sre = b_renames->items[i].util; path_list_insert(sre->pair->two->path, &b_by_dst)->util = sre->dst_entry; } for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) { int compare; char *src; struct path_list *renames1, *renames2, *renames2Dst; struct rename *ren1 = NULL, *ren2 = NULL; const char *branch1, *branch2; const char *ren1_src, *ren1_dst; if (i >= a_renames->nr) { compare = 1; ren2 = b_renames->items[j++].util; } else if (j >= b_renames->nr) { compare = -1; ren1 = a_renames->items[i++].util; } else { compare = strcmp(a_renames->items[i].path, b_renames->items[j].path); if (compare <= 0) ren1 = a_renames->items[i++].util; if (compare >= 0) ren2 = b_renames->items[j++].util; } /* TODO: refactor, so that 1/2 are not needed */ if (ren1) { renames1 = a_renames; renames2 = b_renames; renames2Dst = &b_by_dst; branch1 = a_branch; branch2 = b_branch; } else { struct rename *tmp; renames1 = b_renames; renames2 = a_renames; renames2Dst = &a_by_dst; branch1 = b_branch; branch2 = a_branch; tmp = ren2; ren2 = ren1; ren1 = tmp; } src = ren1->pair->one->path; ren1->dst_entry->processed = 1; ren1->src_entry->processed = 1; if (ren1->processed) continue; ren1->processed = 1; ren1_src = ren1->pair->one->path; ren1_dst = ren1->pair->two->path; if (ren2) { const char *ren2_src = ren2->pair->one->path; const char *ren2_dst = ren2->pair->two->path; /* Renamed in 1 and renamed in 2 */ if (strcmp(ren1_src, ren2_src) != 0) die("ren1.src != ren2.src"); ren2->dst_entry->processed = 1; ren2->processed = 1; if (strcmp(ren1_dst, ren2_dst) != 0) { clean_merge = 0; output(1, "CONFLICT (rename/rename): " "Rename \"%s\"->\"%s\" in branch \"%s\" " "rename \"%s\"->\"%s\" in \"%s\"%s", src, ren1_dst, branch1, src, ren2_dst, branch2, index_only ? " (left unresolved)": ""); if (index_only) { remove_file_from_cache(src); update_file(0, ren1->pair->one->sha1, ren1->pair->one->mode, src); } conflict_rename_rename(ren1, branch1, ren2, branch2); } else { struct merge_file_info mfi; remove_file(1, ren1_src, 1); mfi = merge_file(ren1->pair->one, ren1->pair->two, ren2->pair->two, branch1, branch2); if (mfi.merge || !mfi.clean) output(1, "Renamed %s->%s", src, ren1_dst); if (mfi.merge) output(2, "Auto-merged %s", ren1_dst); if (!mfi.clean) { output(1, "CONFLICT (content): merge conflict in %s", ren1_dst); clean_merge = 0; if (!index_only) update_stages(ren1_dst, ren1->pair->one, ren1->pair->two, ren2->pair->two, 1 /* clear */); } update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst); } } else { /* Renamed in 1, maybe changed in 2 */ struct path_list_item *item; /* we only use sha1 and mode of these */ struct diff_filespec src_other, dst_other; int try_merge, stage = a_renames == renames1 ? 3: 2; remove_file(1, ren1_src, index_only || stage == 3); hashcpy(src_other.sha1, ren1->src_entry->stages[stage].sha); src_other.mode = ren1->src_entry->stages[stage].mode; hashcpy(dst_other.sha1, ren1->dst_entry->stages[stage].sha); dst_other.mode = ren1->dst_entry->stages[stage].mode; try_merge = 0; if (path_list_has_path(¤t_directory_set, ren1_dst)) { clean_merge = 0; output(1, "CONFLICT (rename/directory): Renamed %s->%s in %s " " directory %s added in %s", ren1_src, ren1_dst, branch1, ren1_dst, branch2); conflict_rename_dir(ren1, branch1); } else if (sha_eq(src_other.sha1, null_sha1)) { clean_merge = 0; output(1, "CONFLICT (rename/delete): Renamed %s->%s in %s " "and deleted in %s", ren1_src, ren1_dst, branch1, branch2); update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst); } else if (!sha_eq(dst_other.sha1, null_sha1)) { const char *new_path; clean_merge = 0; try_merge = 1; output(1, "CONFLICT (rename/add): Renamed %s->%s in %s. " "%s added in %s", ren1_src, ren1_dst, branch1, ren1_dst, branch2); new_path = unique_path(ren1_dst, branch2); output(1, "Added as %s instead", new_path); update_file(0, dst_other.sha1, dst_other.mode, new_path); } else if ((item = path_list_lookup(ren1_dst, renames2Dst))) { ren2 = item->util; clean_merge = 0; ren2->processed = 1; output(1, "CONFLICT (rename/rename): Renamed %s->%s in %s. " "Renamed %s->%s in %s", ren1_src, ren1_dst, branch1, ren2->pair->one->path, ren2->pair->two->path, branch2); conflict_rename_rename_2(ren1, branch1, ren2, branch2); } else try_merge = 1; if (try_merge) { struct diff_filespec *o, *a, *b; struct merge_file_info mfi; src_other.path = (char *)ren1_src; o = ren1->pair->one; if (a_renames == renames1) { a = ren1->pair->two; b = &src_other; } else { b = ren1->pair->two; a = &src_other; } mfi = merge_file(o, a, b, a_branch, b_branch); if (mfi.clean && sha_eq(mfi.sha, ren1->pair->two->sha1) && mfi.mode == ren1->pair->two->mode) /* * This messaged is part of * t6022 test. If you change * it update the test too. */ output(3, "Skipped %s (merged same as existing)", ren1_dst); else { if (mfi.merge || !mfi.clean) output(1, "Renamed %s => %s", ren1_src, ren1_dst); if (mfi.merge) output(2, "Auto-merged %s", ren1_dst); if (!mfi.clean) { output(1, "CONFLICT (rename/modify): Merge conflict in %s", ren1_dst); clean_merge = 0; if (!index_only) update_stages(ren1_dst, o, a, b, 1); } update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst); } } } } path_list_clear(&a_by_dst, 0); path_list_clear(&b_by_dst, 0); return clean_merge; }
/* * This is called during restore to create the file (if necessary) We must return in rp->create_status: * * CF_ERROR -- error * CF_SKIP -- skip processing this file * CF_EXTRACT -- extract the file (i.e.call i/o routines) * CF_CREATED -- created, but no content to extract (typically directories) */ static bRC createFile(bpContext *ctx, struct restore_pkt *rp) { int status; bool exists = false; struct stat st; plugin_ctx *p_ctx = (plugin_ctx *)ctx->pContext; if (!p_ctx) { return bRC_Error; } /* * See if the file already exists. */ Dmsg2(400, "Replace=%c %d\n", (char)rp->replace, rp->replace); status = ceph_lstat(p_ctx->cmount, rp->ofname, &st); if (status == 0) { exists = true; switch (rp->replace) { case REPLACE_IFNEWER: if (rp->statp.st_mtime <= st.st_mtime) { Jmsg(ctx, M_SKIPPED, 0, _("File skipped. Not newer: %s\n"), rp->ofname); rp->create_status = CF_SKIP; goto bail_out; } break; case REPLACE_IFOLDER: if (rp->statp.st_mtime >= st.st_mtime) { Jmsg(ctx, M_SKIPPED, 0, _("File skipped. Not older: %s\n"), rp->ofname); rp->create_status = CF_SKIP; goto bail_out; } break; case REPLACE_NEVER: /* * Set attributes if we created this directory */ if (rp->type == FT_DIREND && path_list_lookup(p_ctx->path_list, rp->ofname)) { break; } Jmsg(ctx, M_SKIPPED, 0, _("File skipped. Already exists: %s\n"), rp->ofname); rp->create_status = CF_SKIP; goto bail_out; case REPLACE_ALWAYS: break; } } switch (rp->type) { case FT_LNKSAVED: /* Hard linked, file already saved */ case FT_LNK: case FT_SPEC: /* Fifo, ... to be backed up */ case FT_REGE: /* Empty file */ case FT_REG: /* Regular file */ /* * See if file already exists then we need to unlink it. */ if (exists) { Dmsg1(400, "unlink %s\n", rp->ofname); status = ceph_unlink(p_ctx->cmount, rp->ofname); if (status != 0) { berrno be; Jmsg(ctx, M_ERROR, 0, _("File %s already exists and could not be replaced. ERR=%s.\n"), rp->ofname, be.bstrerror(-status)); /* * Continue despite error */ } } else { /* * File doesn't exist see if we need to create the parent directory. */ POOL_MEM parent_dir(PM_FNAME); char *bp; pm_strcpy(parent_dir, rp->ofname); bp = strrchr(parent_dir.c_str(), '/'); if (bp) { *bp = '\0'; if (strlen(parent_dir.c_str())) { if (!cephfs_makedirs(p_ctx, parent_dir.c_str())) { rp->create_status = CF_ERROR; goto bail_out; } } } } /* * See if we need to perform anything special for the restore file type. */ switch (rp->type) { case FT_LNKSAVED: status = ceph_link(p_ctx->cmount, rp->olname, rp->ofname); if (status < 0) { berrno be; Jmsg(ctx, M_ERROR, "ceph_link(%s) failed: %s\n", rp->ofname, be.bstrerror(-status)); rp->create_status = CF_ERROR; } else { rp->create_status = CF_CREATED; } break; case FT_LNK: status = ceph_symlink(p_ctx->cmount, rp->olname, rp->ofname); if (status < 0) { berrno be; Jmsg(ctx, M_ERROR, "ceph_symlink(%s) failed: %s\n", rp->ofname, be.bstrerror(-status)); rp->create_status = CF_ERROR; } else { rp->create_status = CF_CREATED; } break; case FT_SPEC: status = ceph_mknod(p_ctx->cmount, rp->olname, rp->statp.st_mode, rp->statp.st_rdev); if (status < 0) { berrno be; Jmsg(ctx, M_ERROR, "ceph_mknod(%s) failed: %s\n", rp->ofname, be.bstrerror(-status)); rp->create_status = CF_ERROR; } else { rp->create_status = CF_CREATED; } break; default: rp->create_status = CF_EXTRACT; break; } break; case FT_DIRBEGIN: case FT_DIREND: if (!cephfs_makedirs(p_ctx, rp->ofname)) { rp->create_status = CF_ERROR; } else { rp->create_status = CF_CREATED; } break; case FT_DELETED: Jmsg(ctx, M_INFO, 0, _("Original file %s have been deleted: type=%d\n"), rp->ofname, rp->type); break; default: Jmsg(ctx, M_ERROR, 0, _("Unknown file type %d; not restored: %s\n"), rp->type, rp->ofname); break; } bail_out: return bRC_OK; }
/* * Create the file, or the directory * * fname is the original filename * ofile is the output filename (may be in a different directory) * * Returns: CF_SKIP if file should be skipped * CF_ERROR on error * CF_EXTRACT file created and data to restore * CF_CREATED file created no data to restore * * Note, we create the file here, except for special files, * we do not set the attributes because we want to first * write the file, then when the writing is done, set the * attributes. * * So, we return with the file descriptor open for normal files. */ int create_file(JCR *jcr, ATTR *attr, BFILE *bfd, int replace) { mode_t new_mode, parent_mode; int flags; uid_t uid; gid_t gid; int pnl; bool exists = false; struct stat mstatp; #ifndef HAVE_WIN32 bool isOnRoot; #endif bfd->reparse_point = false; if (is_win32_stream(attr->data_stream)) { set_win32_backup(bfd); } else { set_portable_backup(bfd); } new_mode = attr->statp.st_mode; Dmsg3(200, "type=%d newmode=%x file=%s\n", attr->type, new_mode, attr->ofname); parent_mode = S_IWUSR | S_IXUSR | new_mode; gid = attr->statp.st_gid; uid = attr->statp.st_uid; #ifdef HAVE_WIN32 if (!bfd->use_backup_api) { /* * Eliminate invalid windows filename characters from foreign filenames */ char *ch = (char *)attr->ofname; if (ch[0] != 0 && ch[1] != 0) { ch += 2; while (*ch) { switch (*ch) { case ':': case '<': case '>': case '*': case '?': case '|': *ch = '_'; break; } ch++; } } } #endif Dmsg2(400, "Replace=%c %d\n", (char)replace, replace); if (lstat(attr->ofname, &mstatp) == 0) { exists = true; switch (replace) { case REPLACE_IFNEWER: if (attr->statp.st_mtime <= mstatp.st_mtime) { Qmsg(jcr, M_INFO, 0, _("File skipped. Not newer: %s\n"), attr->ofname); return CF_SKIP; } break; case REPLACE_IFOLDER: if (attr->statp.st_mtime >= mstatp.st_mtime) { Qmsg(jcr, M_INFO, 0, _("File skipped. Not older: %s\n"), attr->ofname); return CF_SKIP; } break; case REPLACE_NEVER: /* * Set attributes if we created this directory */ if (attr->type == FT_DIREND && path_list_lookup(jcr->path_list, attr->ofname)) { break; } Qmsg(jcr, M_INFO, 0, _("File skipped. Already exists: %s\n"), attr->ofname); return CF_SKIP; case REPLACE_ALWAYS: break; } } switch (attr->type) { case FT_RAW: /* Raw device to be written */ case FT_FIFO: /* FIFO to be written to */ case FT_LNKSAVED: /* Hard linked, file already saved */ case FT_LNK: case FT_SPEC: /* Fifo, ... to be backed up */ case FT_REGE: /* Empty file */ case FT_REG: /* Regular file */ /* * Note, we do not delete FT_RAW because these are device files * or FIFOs that should already exist. If we blow it away, * we may blow away a FIFO that is being used to read the * restore data, or we may blow away a partition definition. */ if (exists && attr->type != FT_RAW && attr->type != FT_FIFO) { /* Get rid of old copy */ Dmsg1(400, "unlink %s\n", attr->ofname); if (secure_erase(jcr, attr->ofname) == -1) { berrno be; Qmsg(jcr, M_ERROR, 0, _("File %s already exists and could not be replaced. ERR=%s.\n"), attr->ofname, be.bstrerror()); /* Continue despite error */ } } /* * Here we do some preliminary work for all the above * types to create the path to the file if it does * not already exist. Below, we will split to * do the file type specific work */ pnl = separate_path_and_file(jcr, attr->fname, attr->ofname); if (pnl < 0) { return CF_ERROR; } /* * If path length is <= 0 we are making a file in the root * directory. Assume that the directory already exists. */ if (pnl > 0) { char savechr; savechr = attr->ofname[pnl]; attr->ofname[pnl] = 0; /* terminate path */ if (!path_already_seen(jcr, attr->ofname, pnl)) { Dmsg1(400, "Make path %s\n", attr->ofname); /* * If we need to make the directory, ensure that it is with * execute bit set (i.e. parent_mode), and preserve what already * exists. Normally, this should do nothing. */ if (!makepath(attr, attr->ofname, parent_mode, parent_mode, uid, gid, 1)) { Dmsg1(10, "Could not make path. %s\n", attr->ofname); attr->ofname[pnl] = savechr; /* restore full name */ return CF_ERROR; } } attr->ofname[pnl] = savechr; /* restore full name */ } /* * Now we do the specific work for each file type */ switch(attr->type) { case FT_REGE: case FT_REG: Dmsg1(100, "Create=%s\n", attr->ofname); flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY; /* O_NOFOLLOW; */ if (IS_CTG(attr->statp.st_mode)) { flags |= O_CTG; /* set contiguous bit if needed */ } if (is_bopen(bfd)) { Qmsg1(jcr, M_ERROR, 0, _("bpkt already open fid=%d\n"), bfd->fid); bclose(bfd); } if (bopen(bfd, attr->ofname, flags, 0, attr->statp.st_rdev) < 0) { berrno be; be.set_errno(bfd->berrno); Qmsg2(jcr, M_ERROR, 0, _("Could not create %s: ERR=%s\n"), attr->ofname, be.bstrerror()); Dmsg2(100,"Could not create %s: ERR=%s\n", attr->ofname, be.bstrerror()); return CF_ERROR; } return CF_EXTRACT; #ifndef HAVE_WIN32 /* None of these exist in MS Windows */ case FT_RAW: /* Bareos raw device e.g. /dev/sda1 */ case FT_FIFO: /* Bareos fifo to save data */ case FT_SPEC: flags = O_WRONLY | O_BINARY; isOnRoot = bstrcmp(attr->fname, attr->ofname) ? 1 : 0; if (S_ISFIFO(attr->statp.st_mode)) { Dmsg1(400, "Restore fifo: %s\n", attr->ofname); if (mkfifo(attr->ofname, attr->statp.st_mode) != 0 && errno != EEXIST) { berrno be; Qmsg2(jcr, M_ERROR, 0, _("Cannot make fifo %s: ERR=%s\n"), attr->ofname, be.bstrerror()); return CF_ERROR; } } else if (S_ISSOCK(attr->statp.st_mode)) { Dmsg1(200, "Skipping restore of socket: %s\n", attr->ofname); #ifdef S_IFDOOR /* Solaris high speed RPC mechanism */ } else if (S_ISDOOR(attr->statp.st_mode)) { Dmsg1(200, "Skipping restore of door file: %s\n", attr->ofname); #endif #ifdef S_IFPORT /* Solaris event port for handling AIO */ } else if (S_ISPORT(attr->statp.st_mode)) { Dmsg1(200, "Skipping restore of event port file: %s\n", attr->ofname); #endif } else if ((S_ISBLK(attr->statp.st_mode) || S_ISCHR(attr->statp.st_mode)) && !exists && isOnRoot) { /* * Fatal: Restoring a device on root-file system, but device node does not exist. * Should not create a dump file. */ Qmsg1(jcr, M_ERROR, 0, _("Device restore on root failed, device %s missing.\n"), attr->fname); return CF_ERROR; } else if (S_ISBLK(attr->statp.st_mode) || S_ISCHR(attr->statp.st_mode)) { Dmsg1(400, "Restoring a device as a file: %s\n", attr->ofname); flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY; } else { Dmsg1(400, "Restore node: %s\n", attr->ofname); if (mknod(attr->ofname, attr->statp.st_mode, attr->statp.st_rdev) != 0 && errno != EEXIST) { berrno be; Qmsg2(jcr, M_ERROR, 0, _("Cannot make node %s: ERR=%s\n"), attr->ofname, be.bstrerror()); return CF_ERROR; } } /* * Here we are going to attempt to restore to a FIFO, which * means that the FIFO must already exist, AND there must * be some process already attempting to read from the * FIFO, so we open it write-only. */ if (attr->type == FT_RAW || attr->type == FT_FIFO) { btimer_t *tid; Dmsg1(400, "FT_RAW|FT_FIFO %s\n", attr->ofname); /* * Timeout open() in 60 seconds */ if (attr->type == FT_FIFO) { Dmsg0(400, "Set FIFO timer\n"); tid = start_thread_timer(jcr, pthread_self(), 60); } else { tid = NULL; } if (is_bopen(bfd)) { Qmsg1(jcr, M_ERROR, 0, _("bpkt already open fid=%d\n"), bfd->fid); } Dmsg2(400, "open %s flags=0x%x\n", attr->ofname, flags); if ((bopen(bfd, attr->ofname, flags, 0, 0)) < 0) { berrno be; be.set_errno(bfd->berrno); Qmsg2(jcr, M_ERROR, 0, _("Could not open %s: ERR=%s\n"), attr->ofname, be.bstrerror()); Dmsg2(400, "Could not open %s: ERR=%s\n", attr->ofname, be.bstrerror()); stop_thread_timer(tid); return CF_ERROR; } stop_thread_timer(tid); return CF_EXTRACT; } Dmsg1(400, "FT_SPEC %s\n", attr->ofname); return CF_CREATED; case FT_LNKSAVED: /* Hard linked, file already saved */ Dmsg2(130, "Hard link %s => %s\n", attr->ofname, attr->olname); if (link(attr->olname, attr->ofname) != 0) { berrno be; #ifdef HAVE_CHFLAGS struct stat s; /* * If using BSD user flags, maybe has a file flag preventing this. * So attempt to disable, retry link, and reset flags. * Note that BSD securelevel may prevent disabling flag. */ if (stat(attr->olname, &s) == 0 && s.st_flags != 0) { if (chflags(attr->olname, 0) == 0) { if (link(attr->olname, attr->ofname) != 0) { /* * Restore original file flags even when linking failed */ if (chflags(attr->olname, s.st_flags) < 0) { Qmsg2(jcr, M_ERROR, 0, _("Could not restore file flags for file %s: ERR=%s\n"), attr->olname, be.bstrerror()); } #endif /* HAVE_CHFLAGS */ Qmsg3(jcr, M_ERROR, 0, _("Could not hard link %s -> %s: ERR=%s\n"), attr->ofname, attr->olname, be.bstrerror()); Dmsg3(200, "Could not hard link %s -> %s: ERR=%s\n", attr->ofname, attr->olname, be.bstrerror()); return CF_ERROR; #ifdef HAVE_CHFLAGS } /* * Finally restore original file flags */ if (chflags(attr->olname, s.st_flags) < 0) { Qmsg2(jcr, M_ERROR, 0, _("Could not restore file flags for file %s: ERR=%s\n"), attr->olname, be.bstrerror()); } } else { Qmsg2(jcr, M_ERROR, 0, _("Could not reset file flags for file %s: ERR=%s\n"), attr->olname, be.bstrerror()); } } else { Qmsg3(jcr, M_ERROR, 0, _("Could not hard link %s -> %s: ERR=%s\n"), attr->ofname, attr->olname, be.bstrerror()); return CF_ERROR; } #endif /* HAVE_CHFLAGS */ } return CF_CREATED; #endif /* HAVE_WIN32 */ #ifdef HAVE_WIN32 case FT_LNK: /* * Handle Windows Symlink-Like Reparse Points * - Directory Symlinks * - File Symlinks * - Volume Mount Points * - Junctions */ Dmsg2(130, "FT_LNK should restore: %s -> %s\n", attr->ofname, attr->olname); if (attr->statp.st_rdev & FILE_ATTRIBUTE_VOLUME_MOUNT_POINT) { /* * We do not restore volume mount points */ Dmsg0(130, "Skipping Volume Mount Point\n"); return CF_SKIP; } if (win32_symlink(attr->olname, attr->ofname, attr->statp.st_rdev) != 0 && errno != EEXIST) { berrno be; Qmsg3(jcr, M_ERROR, 0, _("Could not symlink %s -> %s: ERR=%s\n"), attr->ofname, attr->olname, be.bstrerror()); return CF_ERROR; } return CF_CREATED; #else case FT_LNK: /* * Unix/Linux symlink handling */ Dmsg2(130, "FT_LNK should restore: %s -> %s\n", attr->ofname, attr->olname); if (symlink(attr->olname, attr->ofname) != 0 && errno != EEXIST) { berrno be; Qmsg3(jcr, M_ERROR, 0, _("Could not symlink %s -> %s: ERR=%s\n"), attr->ofname, attr->olname, be.bstrerror()); return CF_ERROR; } return CF_CREATED; #endif } /* End inner switch */ case FT_REPARSE: case FT_JUNCTION: bfd->reparse_point = true; /* * Fall through wanted */ case FT_DIRBEGIN: case FT_DIREND: Dmsg2(200, "Make dir mode=%o dir=%s\n", new_mode, attr->ofname); if (!makepath(attr, attr->ofname, new_mode, parent_mode, uid, gid, 0)) { return CF_ERROR; } /* * If we are using the Win32 Backup API, we open the directory so * that the security info will be read and saved. */ if (!is_portable_backup(bfd)) { if (is_bopen(bfd)) { Qmsg1(jcr, M_ERROR, 0, _("bpkt already open fid=%d\n"), bfd->fid); } if (bopen(bfd, attr->ofname, O_WRONLY | O_BINARY, 0, attr->statp.st_rdev) < 0) { berrno be; be.set_errno(bfd->berrno); #ifdef HAVE_WIN32 /* * Check for trying to create a drive, if so, skip */ if (attr->ofname[1] == ':' && IsPathSeparator(attr->ofname[2]) && attr->ofname[3] == '\0') { return CF_SKIP; } #endif Qmsg2(jcr, M_ERROR, 0, _("Could not open %s: ERR=%s\n"), attr->ofname, be.bstrerror()); return CF_ERROR; } return CF_EXTRACT; } else { return CF_CREATED; } case FT_DELETED: Qmsg2(jcr, M_INFO, 0, _("Original file %s have been deleted: type=%d\n"), attr->fname, attr->type); break; /* * The following should not occur */ case FT_NOACCESS: case FT_NOFOLLOW: case FT_NOSTAT: case FT_DIRNOCHG: case FT_NOCHG: case FT_ISARCH: case FT_NORECURSE: case FT_NOFSCHG: case FT_NOOPEN: Qmsg2(jcr, M_ERROR, 0, _("Original file %s not saved: type=%d\n"), attr->fname, attr->type); break; default: Qmsg2(jcr, M_ERROR, 0, _("Unknown file type %d; not restored: %s\n"), attr->type, attr->fname); break; } return CF_ERROR; }