X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fdird%2Fua_tree.c;h=a03eb1b15845144e390f2850923317bfe800c894;hb=649ac682e935fd1b20bec0d3248a45d7925bee6a;hp=d3df13ac16af3b473c37a791da8a43072487e332;hpb=f3a77d749c0a682ded1f80b5c75c14f97d89b1a6;p=bacula%2Fbacula diff --git a/bacula/src/dird/ua_tree.c b/bacula/src/dird/ua_tree.c index d3df13ac16..a03eb1b158 100644 --- a/bacula/src/dird/ua_tree.c +++ b/bacula/src/dird/ua_tree.c @@ -1,39 +1,27 @@ /* - Bacula® - The Network Backup Solution - - Copyright (C) 2002-2009 Free Software Foundation Europe e.V. - - The main author of Bacula is Kern Sibbald, with contributions from - many others, a complete list can be found in the file AUTHORS. - This program is Free Software; you can redistribute it and/or - modify it under the terms of version two of the GNU General Public - License as published by the Free Software Foundation and included - in the file LICENSE. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. - - Bacula® is a registered trademark of Kern Sibbald. - The licensor of Bacula is the Free Software Foundation Europe - (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, - Switzerland, email:ftf@fsfeurope.org. + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2017 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. */ /* - * * Bacula Director -- User Agent Database File tree for Restore * command. This file interacts with the user implementing the * UA tree commands. * * Kern Sibbald, July MMII - * - * Version $Id$ */ #include "bacula.h" @@ -65,7 +53,10 @@ static int unmarkcmd(UAContext *ua, TREE_CTX *tree); static int unmarkdircmd(UAContext *ua, TREE_CTX *tree); static int quitcmd(UAContext *ua, TREE_CTX *tree); static int donecmd(UAContext *ua, TREE_CTX *tree); - +static int dot_lsdircmd(UAContext *ua, TREE_CTX *tree); +static int dot_lscmd(UAContext *ua, TREE_CTX *tree); +static int dot_helpcmd(UAContext *ua, TREE_CTX *tree); +static int dot_lsmarkcmd(UAContext *ua, TREE_CTX *tree); struct cmdstruct { const char *key; int (*func)(UAContext *ua, TREE_CTX *tree); const char *help; }; static struct cmdstruct commands[] = { @@ -81,7 +72,10 @@ static struct cmdstruct commands[] = { { NT_("find"), findcmd, _("find files, wildcards allowed")}, { NT_("help"), helpcmd, _("print help")}, { NT_("ls"), lscmd, _("list current directory, wildcards allowed")}, + { NT_(".ls"), dot_lscmd, _("list current directory, wildcards allowed")}, + { NT_(".lsdir"), dot_lsdircmd, _("list subdir in current directory, wildcards allowed")}, { NT_("lsmark"), lsmarkcmd, _("list the marked files in and below the cd")}, + { NT_(".lsmark"), dot_lsmarkcmd,_("list the marked files in")}, { NT_("mark"), markcmd, _("mark dir/file to be restored recursively, wildcards allowed")}, { NT_("markdir"), markdircmd, _("mark directory name to be restored (no files)")}, { NT_("pwd"), pwdcmd, _("print current working directory")}, @@ -89,6 +83,7 @@ static struct cmdstruct commands[] = { { NT_("unmark"), unmarkcmd, _("unmark dir/file to be restored recursively in dir")}, { NT_("unmarkdir"), unmarkdircmd, _("unmark directory name only no recursion")}, { NT_("quit"), quitcmd, _("quit and do not do restore")}, + { NT_(".help"), dot_helpcmd, _("print help")}, { NT_("?"), helpcmd, _("print help")}, }; #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct))) @@ -123,13 +118,13 @@ bool user_select_files_from_tree(TREE_CTX *tree) ua->send_msg(_("cwd is: %s\n"), cwd); for ( ;; ) { int found, len, i; - if (!get_cmd(ua, "$ ")) { + if (!get_cmd(ua, "$ ", true)) { break; } if (ua->api) user->signal(BNET_CMD_BEGIN); parse_args_only(ua->cmd, &ua->args, &ua->argc, ua->argk, ua->argv, MAX_CMD_ARGS); if (ua->argc == 0) { - ua->warning_msg(_("Invalid command \"%s\". Enter \"done\" to exit.\n"), ua->cmd); + ua->warning_msg(_("Invalid command \"%s\". Enter \"done\" to exit.\n"), ua->cmd); if (ua->api) user->signal(BNET_CMD_FAILED); continue; } @@ -138,13 +133,17 @@ bool user_select_files_from_tree(TREE_CTX *tree) found = 0; stat = false; for (i=0; iargk[0], _(commands[i].key), len) == 0) { + if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) { stat = (*commands[i].func)(ua, tree); /* go execute command */ found = 1; break; } if (!found) { - ua->warning_msg(_("Invalid command \"%s\". Enter \"done\" to exit.\n"), ua->cmd); + if (*ua->argk[0] == '.') { + /* Some unknow dot command -- probably .messages, ignore it */ + continue; + } + ua->warning_msg(_("Invalid command \"%s\". Enter \"done\" to exit.\n"), ua->cmd); if (ua->api) user->signal(BNET_CMD_FAILED); continue; } @@ -161,7 +160,6 @@ bool user_select_files_from_tree(TREE_CTX *tree) return stat; } - /* * This callback routine is responsible for inserting the * items it gets into the directory tree. For each JobId selected @@ -171,7 +169,7 @@ bool user_select_files_from_tree(TREE_CTX *tree) * * See uar_sel_files in sql_cmds.c for query that calls us. * row[0]=Path, row[1]=Filename, row[2]=FileIndex - * row[3]=JobId row[4]=LStat + * row[3]=JobId row[4]=LStat row[5]=DeltaSeq */ int insert_tree_handler(void *ctx, int num_fields, char **row) { @@ -181,10 +179,13 @@ int insert_tree_handler(void *ctx, int num_fields, char **row) int type; bool hard_link, ok; int FileIndex; + int32_t delta_seq; JobId_t JobId; + HL_ENTRY *entry = NULL; + int32_t LinkFI; - Dmsg4(400, "Path=%s%s FI=%s JobId=%s\n", row[0], row[1], - row[2], row[3]); + Dmsg4(150, "Path=%s%s FI=%s JobId=%s\n", row[0], row[1], + row[2], row[3]); if (*row[1] == 0) { /* no filename => directory */ if (!IsPathSeparator(*row[0])) { /* Must be Win32 directory */ type = TN_DIR_NLS; @@ -194,11 +195,37 @@ int insert_tree_handler(void *ctx, int num_fields, char **row) } else { type = TN_FILE; } - hard_link = (decode_LinkFI(row[4], &statp) != 0); + decode_stat(row[4], &statp, sizeof(statp), &LinkFI); + + hard_link = (LinkFI != 0); node = insert_tree_node(row[0], row[1], type, tree->root, NULL); JobId = str_to_int64(row[3]); FileIndex = str_to_int64(row[2]); - Dmsg2(400, "JobId=%s FileIndex=%s\n", row[3], row[2]); + delta_seq = str_to_int64(row[5]); + Dmsg6(150, "node=0x%p JobId=%s FileIndex=%s Delta=%s node.delta=%d LinkFI=%d\n", + node, row[3], row[2], row[5], node->delta_seq, LinkFI); + + /* TODO: check with hardlinks */ + if (delta_seq > 0) { + if (delta_seq == (node->delta_seq + 1)) { + tree_add_delta_part(tree->root, node, node->JobId, node->FileIndex); + + } else { + /* File looks to be deleted */ + if (node->delta_seq == -1) { /* just created */ + tree_remove_node(tree->root, node); + + } else { + tree->ua->warning_msg(_("Something is wrong with the Delta sequence of %s, " + "skipping new parts. Current sequence is %d\n"), + row[1], node->delta_seq); + + Dmsg3(0, "Something is wrong with Delta, skip it " + "fname=%s d1=%d d2=%d\n", row[1], node->delta_seq, delta_seq); + } + return 0; + } + } /* * - The first time we see a file (node->inserted==true), we accept it. * - In the same JobId, we accept only the first copy of a @@ -222,12 +249,35 @@ int insert_tree_handler(void *ctx, int num_fields, char **row) node->JobId = JobId; node->type = type; node->soft_link = S_ISLNK(statp.st_mode) != 0; + node->delta_seq = delta_seq; + node->can_access = true; if (tree->all) { node->extract = true; /* extract all by default */ if (type == TN_DIR || type == TN_DIR_NLS) { node->extract_dir = true; /* if dir, extract it */ } } + /* insert file having hardlinks into hardlink hashtable */ + if (statp.st_nlink > 1 && type != TN_DIR && type != TN_DIR_NLS) { + if (!LinkFI) { + /* first occurrence - file hardlinked to */ + entry = (HL_ENTRY *)tree->root->hardlinks.hash_malloc(sizeof(HL_ENTRY)); + entry->key = (((uint64_t) JobId) << 32) + FileIndex; + entry->node = node; + tree->root->hardlinks.insert(entry->key, entry); + } else if (tree->hardlinks_in_mem) { + /* hardlink to known file index: lookup original file */ + uint64_t file_key = (((uint64_t) JobId) << 32) + LinkFI; + HL_ENTRY *first_hl = (HL_ENTRY *) tree->root->hardlinks.lookup(file_key); + if (first_hl && first_hl->node) { + /* then add hardlink entry to linked node*/ + entry = (HL_ENTRY *)tree->root->hardlinks.hash_malloc(sizeof(HL_ENTRY)); + entry->key = (((uint64_t) JobId) << 32) + FileIndex; + entry->node = first_hl->node; + tree->root->hardlinks.insert(entry->key, entry); + } + } + } } if (node->inserted) { tree->FileCount++; @@ -240,7 +290,6 @@ int insert_tree_handler(void *ctx, int num_fields, char **row) return 0; } - /* * Set extract to value passed. We recursively walk * down the tree setting all children if the @@ -249,8 +298,6 @@ int insert_tree_handler(void *ctx, int num_fields, char **row) static int set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, bool extract) { TREE_NODE *n; - FILE_DBR fdbr; - struct stat statp; int count = 0; node->extract = extract; @@ -270,40 +317,48 @@ static int set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, bool extr * Walk up tree marking any unextracted parent to be * extracted. */ - if (extract) { + if (!tree->no_auto_parent && extract) { while (node->parent && !node->parent->extract_dir) { node = node->parent; node->extract_dir = true; } } } else if (extract) { - char cwd[2000]; - /* - * Ordinary file, we get the full path, look up the - * attributes, decode them, and if we are hard linked to - * a file that was saved, we must load that file too. - */ - tree_getpath(node, cwd, sizeof(cwd)); - fdbr.FileId = 0; - fdbr.JobId = node->JobId; - if (node->hard_link && db_get_file_attributes_record(ua->jcr, ua->db, cwd, NULL, &fdbr)) { - int32_t LinkFI; - decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */ + uint64_t key = 0; + if (tree->hardlinks_in_mem) { + if (node->hard_link) { + key = (((uint64_t) node->JobId) << 32) + node->FileIndex; /* every hardlink is in hashtable, and it points to linked file */ + } + } else { + /* Get the hard link if it exists */ + FILE_DBR fdbr; + struct stat statp; + char cwd[2000]; /* - * If we point to a hard linked file, traverse the tree to - * find that file, and mark it to be restored as well. It - * must have the Link we just obtained and the same JobId. + * Ordinary file, we get the full path, look up the + * attributes, decode them, and if we are hard linked to + * a file that was saved, we must load that file too. */ - if (LinkFI) { - for (n=first_tree_node(tree->root); n; n=next_tree_node(n)) { - if (n->FileIndex == LinkFI && n->JobId == node->JobId) { - n->extract = true; - if (n->type == TN_DIR || n->type == TN_DIR_NLS) { - n->extract_dir = true; - } - break; - } - } + tree_getpath(node, cwd, sizeof(cwd)); + fdbr.FileId = 0; + fdbr.JobId = node->JobId; + if (node->hard_link && db_get_file_attributes_record(ua->jcr, ua->db, cwd, NULL, &fdbr)) { + int32_t LinkFI; + decode_stat(fdbr.LStat, &statp, sizeof(statp), &LinkFI); /* decode stat pkt */ + key = (((uint64_t) node->JobId) << 32) + LinkFI; /* lookup by linked file's fileindex */ + } + } + /* If file hard linked and we have a key */ + if (node->hard_link && key != 0) { + /* + * If we point to a hard linked file, find that file in + * hardlinks hashmap, and mark it to be restored as well. + */ + HL_ENTRY *entry = (HL_ENTRY *)tree->root->hardlinks.lookup(key); + if (entry && entry->node) { + n = entry->node; + n->extract = true; + n->extract_dir = (n->type == TN_DIR || n->type == TN_DIR_NLS); } } } @@ -436,7 +491,52 @@ static int findcmd(UAContext *ua, TREE_CTX *tree) return 1; } +static int dot_lsdircmd(UAContext *ua, TREE_CTX *tree) +{ + TREE_NODE *node; + if (!tree_node_has_child(tree->node)) { + return 1; + } + + foreach_child(node, tree->node) { + if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) { + if (tree_node_has_child(node)) { + ua->send_msg("%s/\n", node->fname); + } + } + } + + return 1; +} + +static int dot_helpcmd(UAContext *ua, TREE_CTX *tree) +{ + for (int i=0; isend_msg("%s\n", commands[i].key); + } + } + return 1; +} + +static int dot_lscmd(UAContext *ua, TREE_CTX *tree) +{ + TREE_NODE *node; + + if (!tree_node_has_child(tree->node)) { + return 1; + } + + foreach_child(node, tree->node) { + if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) { + ua->send_msg("%s%s\n", node->fname, tree_node_has_child(node)?"/":""); + } + } + + return 1; +} static int lscmd(UAContext *ua, TREE_CTX *tree) { @@ -464,12 +564,40 @@ static int lscmd(UAContext *ua, TREE_CTX *tree) /* * Ls command that lists only the marked files */ -static void rlsmark(UAContext *ua, TREE_NODE *tnode) +static int dot_lsmarkcmd(UAContext *ua, TREE_CTX *tree) { TREE_NODE *node; + if (!tree_node_has_child(tree->node)) { + return 1; + } + foreach_child(node, tree->node) { + if ((ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) && + (node->extract || node->extract_dir)) { + ua->send_msg("%s%s\n", node->fname, tree_node_has_child(node)?"/":""); + } + } + return 1; +} + +/* + * This recursive ls command that lists only the marked files + */ +static void rlsmark(UAContext *ua, TREE_NODE *tnode, int level) +{ + TREE_NODE *node; + const int max_level = 100; + char indent[max_level*2+1]; + int i, j; if (!tree_node_has_child(tnode)) { return; } + level = MIN(level, max_level); + j = 0; + for (i=0; iargc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) && (node->extract || node->extract_dir)) { @@ -481,9 +609,9 @@ static void rlsmark(UAContext *ua, TREE_NODE *tnode) } else { tag = ""; } - ua->send_msg("%s%s%s\n", tag, node->fname, tree_node_has_child(node)?"/":""); + ua->send_msg("%s%s%s%s\n", indent, tag, node->fname, tree_node_has_child(node)?"/":""); if (tree_node_has_child(node)) { - rlsmark(ua, node); + rlsmark(ua, node, level+1); } } } @@ -491,15 +619,15 @@ static void rlsmark(UAContext *ua, TREE_NODE *tnode) static int lsmarkcmd(UAContext *ua, TREE_CTX *tree) { - rlsmark(ua, tree->node); + rlsmark(ua, tree->node, 0); return 1; } /* * This is actually the long form used for "dir" */ -static void ls_output(guid_list *guid, char *buf, const char *fname, const char *tag, - struct stat *statp, bool dot_cmd) +static void ls_output(guid_list *guid, char *buf, const char *fname, const char *tag, + struct stat *statp, bool dot_cmd) { char *p; const char *f; @@ -513,7 +641,7 @@ static void ls_output(guid_list *guid, char *buf, const char *fname, const char *p++ = ','; n = sprintf(p, "%d,", (uint32_t)statp->st_nlink); p += n; - n = sprintf(p, "%s,%s,", + n = sprintf(p, "%s,%s,", guid->uid_to_name(statp->st_uid, en1, sizeof(en1)), guid->gid_to_name(statp->st_gid, en2, sizeof(en2))); p += n; @@ -526,11 +654,11 @@ static void ls_output(guid_list *guid, char *buf, const char *fname, const char } else { n = sprintf(p, " %2d ", (uint32_t)statp->st_nlink); p += n; - n = sprintf(p, "%-8.8s %-8.8s", + n = sprintf(p, "%-8.8s %-8.8s", guid->uid_to_name(statp->st_uid, en1, sizeof(en1)), guid->gid_to_name(statp->st_gid, en2, sizeof(en2))); p += n; - n = sprintf(p, "%10.10s ", edit_int64(statp->st_size, ec1)); + n = sprintf(p, "%12.12s ", edit_int64(statp->st_size, ec1)); p += n; if (statp->st_ctime > statp->st_mtime) { time = statp->st_ctime; @@ -598,7 +726,7 @@ static int do_dircmd(UAContext *ua, TREE_CTX *tree, bool dot_cmd) } if (db_get_file_attributes_record(ua->jcr, ua->db, pcwd, NULL, &fdbr)) { int32_t LinkFI; - decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */ + decode_stat(fdbr.LStat, &statp, sizeof(statp), &LinkFI); /* decode stat pkt */ } else { /* Something went wrong getting attributes -- print name */ memset(&statp, 0, sizeof(statp)); @@ -643,7 +771,7 @@ static int estimatecmd(UAContext *ua, TREE_CTX *tree) fdbr.JobId = node->JobId; if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, NULL, &fdbr)) { int32_t LinkFI; - decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */ + decode_stat(fdbr.LStat, &statp, sizeof(statp), &LinkFI); /* decode stat pkt */ if (S_ISREG(statp.st_mode) && statp.st_size > 0) { total_bytes += statp.st_size; } @@ -691,6 +819,7 @@ static int cdcmd(UAContext *ua, TREE_CTX *tree) ua->error_msg(_("Too few or too many arguments. Try using double quotes.\n")); return 1; } + node = tree_cwd(ua->argk[1], tree->root, tree->node); if (!node) { /* Try once more if Win32 drive -- make absolute */ @@ -701,11 +830,14 @@ static int cdcmd(UAContext *ua, TREE_CTX *tree) } if (!node) { ua->warning_msg(_("Invalid path given.\n")); - } else { + } + } + if (node) { + if (node->can_access) { tree->node = node; + } else { + ua->warning_msg(_("Invalid path given. Permission denied.\n")); } - } else { - tree->node = node; } return pwdcmd(ua, tree); }