2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2010 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Director -- User Agent Database File tree for Restore
31 * command. This file interacts with the user implementing the
34 * Kern Sibbald, July MMII
43 #include "lib/fnmatch.h"
45 #include "findlib/find.h"
48 /* Forward referenced commands */
50 static int markcmd(UAContext *ua, TREE_CTX *tree);
51 static int markdircmd(UAContext *ua, TREE_CTX *tree);
52 static int countcmd(UAContext *ua, TREE_CTX *tree);
53 static int findcmd(UAContext *ua, TREE_CTX *tree);
54 static int lscmd(UAContext *ua, TREE_CTX *tree);
55 static int lsmarkcmd(UAContext *ua, TREE_CTX *tree);
56 static int dircmd(UAContext *ua, TREE_CTX *tree);
57 static int dot_dircmd(UAContext *ua, TREE_CTX *tree);
58 static int estimatecmd(UAContext *ua, TREE_CTX *tree);
59 static int helpcmd(UAContext *ua, TREE_CTX *tree);
60 static int cdcmd(UAContext *ua, TREE_CTX *tree);
61 static int pwdcmd(UAContext *ua, TREE_CTX *tree);
62 static int dot_pwdcmd(UAContext *ua, TREE_CTX *tree);
63 static int unmarkcmd(UAContext *ua, TREE_CTX *tree);
64 static int unmarkdircmd(UAContext *ua, TREE_CTX *tree);
65 static int quitcmd(UAContext *ua, TREE_CTX *tree);
66 static int donecmd(UAContext *ua, TREE_CTX *tree);
67 static int dot_lsdircmd(UAContext *ua, TREE_CTX *tree);
68 static int dot_lscmd(UAContext *ua, TREE_CTX *tree);
69 static int dot_helpcmd(UAContext *ua, TREE_CTX *tree);
70 static int dot_lsmarkcmd(UAContext *ua, TREE_CTX *tree);
72 struct cmdstruct { const char *key; int (*func)(UAContext *ua, TREE_CTX *tree); const char *help; };
73 static struct cmdstruct commands[] = {
74 { NT_("add"), markcmd, _("add dir/file to be restored recursively, wildcards allowed")},
75 { NT_("cd"), cdcmd, _("change current directory")},
76 { NT_("count"), countcmd, _("count marked files in and below the cd")},
77 { NT_("delete"), unmarkcmd, _("delete dir/file to be restored recursively in dir")},
78 { NT_("dir"), dircmd, _("long list current directory, wildcards allowed")},
79 { NT_(".dir"), dot_dircmd, _("long list current directory, wildcards allowed")},
80 { NT_("done"), donecmd, _("leave file selection mode")},
81 { NT_("estimate"), estimatecmd, _("estimate restore size")},
82 { NT_("exit"), donecmd, _("same as done command")},
83 { NT_("find"), findcmd, _("find files, wildcards allowed")},
84 { NT_("help"), helpcmd, _("print help")},
85 { NT_("ls"), lscmd, _("list current directory, wildcards allowed")},
86 { NT_(".ls"), dot_lscmd, _("list current directory, wildcards allowed")},
87 { NT_(".lsdir"), dot_lsdircmd, _("list subdir in current directory, wildcards allowed")},
88 { NT_("lsmark"), lsmarkcmd, _("list the marked files in and below the cd")},
89 { NT_(".lsmark"), dot_lsmarkcmd,_("list the marked files in")},
90 { NT_("mark"), markcmd, _("mark dir/file to be restored recursively, wildcards allowed")},
91 { NT_("markdir"), markdircmd, _("mark directory name to be restored (no files)")},
92 { NT_("pwd"), pwdcmd, _("print current working directory")},
93 { NT_(".pwd"), dot_pwdcmd, _("print current working directory")},
94 { NT_("unmark"), unmarkcmd, _("unmark dir/file to be restored recursively in dir")},
95 { NT_("unmarkdir"), unmarkdircmd, _("unmark directory name only no recursion")},
96 { NT_("quit"), quitcmd, _("quit and do not do restore")},
97 { NT_(".help"), dot_helpcmd, _("print help")},
98 { NT_("?"), helpcmd, _("print help")},
100 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
103 * Enter a prompt mode where the user can select/deselect
104 * files to be restored. This is sort of like a mini-shell
105 * that allows "cd", "pwd", "add", "rm", ...
107 bool user_select_files_from_tree(TREE_CTX *tree)
111 /* Get a new context so we don't destroy restore command args */
112 UAContext *ua = new_ua_context(tree->ua->jcr);
113 ua->UA_sock = tree->ua->UA_sock; /* patch in UA socket */
114 ua->api = tree->ua->api; /* keep API flag too */
115 BSOCK *user = ua->UA_sock;
118 "\nYou are now entering file selection mode where you add (mark) and\n"
119 "remove (unmark) files to be restored. No files are initially added, unless\n"
120 "you used the \"all\" keyword on the command line.\n"
121 "Enter \"done\" to leave this mode.\n\n"));
122 if (ua->api) user->signal(BNET_START_RTREE);
124 * Enter interactive command handler allowing selection
125 * of individual files.
127 tree->node = (TREE_NODE *)tree->root;
128 tree_getpath(tree->node, cwd, sizeof(cwd));
129 ua->send_msg(_("cwd is: %s\n"), cwd);
132 if (!get_cmd(ua, "$ ", true)) {
135 if (ua->api) user->signal(BNET_CMD_BEGIN);
136 parse_args_only(ua->cmd, &ua->args, &ua->argc, ua->argk, ua->argv, MAX_CMD_ARGS);
138 ua->warning_msg(_("Invalid command \"%s\". Enter \"done\" to exit.\n"), ua->cmd);
139 if (ua->api) user->signal(BNET_CMD_FAILED);
143 len = strlen(ua->argk[0]);
146 for (i=0; i<comsize; i++) /* search for command */
147 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
148 stat = (*commands[i].func)(ua, tree); /* go execute command */
153 if (*ua->argk[0] == '.') {
154 /* Some unknow dot command -- probably .messages, ignore it */
157 ua->warning_msg(_("Invalid command \"%s\". Enter \"done\" to exit.\n"), ua->cmd);
158 if (ua->api) user->signal(BNET_CMD_FAILED);
161 if (ua->api) user->signal(BNET_CMD_OK);
166 if (ua->api) user->signal(BNET_END_RTREE);
167 ua->UA_sock = NULL; /* don't release restore socket */
170 free_ua_context(ua); /* get rid of temp UA context */
176 * This callback routine is responsible for inserting the
177 * items it gets into the directory tree. For each JobId selected
178 * this routine is called once for each file. We do not allow
179 * duplicate filenames, but instead keep the info from the most
180 * recent file entered (i.e. the JobIds are assumed to be sorted)
182 * See uar_sel_files in sql_cmds.c for query that calls us.
183 * row[0]=Path, row[1]=Filename, row[2]=FileIndex
184 * row[3]=JobId row[4]=LStat row[5]=MarkId
186 int insert_tree_handler(void *ctx, int num_fields, char **row)
189 TREE_CTX *tree = (TREE_CTX *)ctx;
196 Dmsg4(400, "Path=%s%s FI=%s JobId=%s\n", row[0], row[1],
198 if (*row[1] == 0) { /* no filename => directory */
199 if (!IsPathSeparator(*row[0])) { /* Must be Win32 directory */
207 hard_link = (decode_LinkFI(row[4], &statp) != 0);
208 node = insert_tree_node(row[0], row[1], type, tree->root, NULL);
209 JobId = str_to_int64(row[3]);
210 FileIndex = str_to_int64(row[2]);
211 Dmsg2(400, "JobId=%s FileIndex=%s\n", row[3], row[2]);
213 * - The first time we see a file (node->inserted==true), we accept it.
214 * - In the same JobId, we accept only the first copy of a
215 * hard linked file (the others are simply pointers).
216 * - In the same JobId, we accept the last copy of any other
217 * file -- in particular directories.
219 * All the code to set ok could be condensed to a single
220 * line, but it would be even harder to read.
223 if (!node->inserted && JobId == node->JobId) {
224 if ((hard_link && FileIndex > node->FileIndex) ||
225 (!hard_link && FileIndex < node->FileIndex)) {
230 node->hard_link = hard_link;
231 node->FileIndex = FileIndex;
234 node->soft_link = S_ISLNK(statp.st_mode) != 0;
236 node->extract = true; /* extract all by default */
237 if (type == TN_DIR || type == TN_DIR_NLS) {
238 node->extract_dir = true; /* if dir, extract it */
242 if (node->inserted) {
244 if (tree->DeltaCount > 0 && (tree->FileCount-tree->LastCount) > tree->DeltaCount) {
245 tree->ua->send_msg("+");
246 tree->LastCount = tree->FileCount;
255 * Set extract to value passed. We recursively walk
256 * down the tree setting all children if the
257 * node is a directory.
259 static int set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, bool extract)
266 node->extract = extract;
267 if (node->type == TN_DIR || node->type == TN_DIR_NLS) {
268 node->extract_dir = extract; /* set/clear dir too */
270 if (node->type != TN_NEWDIR) {
273 /* For a non-file (i.e. directory), we see all the children */
274 if (node->type != TN_FILE || (node->soft_link && tree_node_has_child(node))) {
275 /* Recursive set children within directory */
276 foreach_child(n, node) {
277 count += set_extract(ua, n, tree, extract);
280 * Walk up tree marking any unextracted parent to be
284 while (node->parent && !node->parent->extract_dir) {
286 node->extract_dir = true;
289 } else if (extract) {
292 * Ordinary file, we get the full path, look up the
293 * attributes, decode them, and if we are hard linked to
294 * a file that was saved, we must load that file too.
296 tree_getpath(node, cwd, sizeof(cwd));
298 fdbr.JobId = node->JobId;
299 if (node->hard_link && db_get_file_attributes_record(ua->jcr, ua->db, cwd, NULL, &fdbr)) {
301 decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
303 * If we point to a hard linked file, traverse the tree to
304 * find that file, and mark it to be restored as well. It
305 * must have the Link we just obtained and the same JobId.
308 for (n=first_tree_node(tree->root); n; n=next_tree_node(n)) {
309 if (n->FileIndex == LinkFI && n->JobId == node->JobId) {
311 if (n->type == TN_DIR || n->type == TN_DIR_NLS) {
312 n->extract_dir = true;
323 static void strip_trailing_slash(char *arg)
325 int len = strlen(arg);
330 if (arg[len] == '/') { /* strip any trailing slash */
336 * Recursively mark the current directory to be restored as
337 * well as all directories and files below it.
339 static int markcmd(UAContext *ua, TREE_CTX *tree)
345 if (ua->argc < 2 || !tree_node_has_child(tree->node)) {
346 ua->send_msg(_("No files marked.\n"));
349 for (int i=1; i < ua->argc; i++) {
350 strip_trailing_slash(ua->argk[i]);
351 foreach_child(node, tree->node) {
352 if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
353 count += set_extract(ua, node, tree, true);
358 ua->send_msg(_("No files marked.\n"));
359 } else if (count == 1) {
360 ua->send_msg(_("1 file marked.\n"));
362 ua->send_msg(_("%s files marked.\n"),
363 edit_uint64_with_commas(count, ec1));
368 static int markdircmd(UAContext *ua, TREE_CTX *tree)
374 if (ua->argc < 2 || !tree_node_has_child(tree->node)) {
375 ua->send_msg(_("No files marked.\n"));
378 for (int i=1; i < ua->argc; i++) {
379 strip_trailing_slash(ua->argk[i]);
380 foreach_child(node, tree->node) {
381 if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
382 if (node->type == TN_DIR || node->type == TN_DIR_NLS) {
383 node->extract_dir = true;
390 ua->send_msg(_("No directories marked.\n"));
391 } else if (count == 1) {
392 ua->send_msg(_("1 directory marked.\n"));
394 ua->send_msg(_("%s directories marked.\n"),
395 edit_uint64_with_commas(count, ec1));
401 static int countcmd(UAContext *ua, TREE_CTX *tree)
403 int total, num_extract;
404 char ec1[50], ec2[50];
406 total = num_extract = 0;
407 for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
408 if (node->type != TN_NEWDIR) {
410 if (node->extract || node->extract_dir) {
415 ua->send_msg(_("%s total files/dirs. %s marked to be restored.\n"),
416 edit_uint64_with_commas(total, ec1),
417 edit_uint64_with_commas(num_extract, ec2));
421 static int findcmd(UAContext *ua, TREE_CTX *tree)
426 ua->send_msg(_("No file specification given.\n"));
427 return 1; /* make it non-fatal */
430 for (int i=1; i < ua->argc; i++) {
431 for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
432 if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
434 tree_getpath(node, cwd, sizeof(cwd));
437 } else if (node->extract_dir) {
442 ua->send_msg("%s%s\n", tag, cwd);
449 static int dot_lsdircmd(UAContext *ua, TREE_CTX *tree)
453 if (!tree_node_has_child(tree->node)) {
457 foreach_child(node, tree->node) {
458 if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) {
459 if (tree_node_has_child(node)) {
460 ua->send_msg("%s/\n", node->fname);
468 static int dot_helpcmd(UAContext *ua, TREE_CTX *tree)
470 for (int i=0; i<comsize; i++) {
471 /* List only non-dot commands */
472 if (commands[i].key[0] != '.') {
473 ua->send_msg("%s\n", commands[i].key);
479 static int dot_lscmd(UAContext *ua, TREE_CTX *tree)
483 if (!tree_node_has_child(tree->node)) {
487 foreach_child(node, tree->node) {
488 if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) {
489 ua->send_msg("%s%s\n", node->fname, tree_node_has_child(node)?"/":"");
496 static int lscmd(UAContext *ua, TREE_CTX *tree)
500 if (!tree_node_has_child(tree->node)) {
503 foreach_child(node, tree->node) {
504 if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) {
508 } else if (node->extract_dir) {
513 ua->send_msg("%s%s%s\n", tag, node->fname, tree_node_has_child(node)?"/":"");
520 * Ls command that lists only the marked files
522 static int dot_lsmarkcmd(UAContext *ua, TREE_CTX *tree)
525 if (!tree_node_has_child(tree->node)) {
528 foreach_child(node, tree->node) {
529 if ((ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) &&
530 (node->extract || node->extract_dir)) {
531 ua->send_msg("%s%s\n", node->fname, tree_node_has_child(node)?"/":"");
538 * Ls command that lists only the marked files
540 static void rlsmark(UAContext *ua, TREE_NODE *tnode)
543 if (!tree_node_has_child(tnode)) {
546 foreach_child(node, tnode) {
547 if ((ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) &&
548 (node->extract || node->extract_dir)) {
552 } else if (node->extract_dir) {
557 ua->send_msg("%s%s%s\n", tag, node->fname, tree_node_has_child(node)?"/":"");
558 if (tree_node_has_child(node)) {
565 static int lsmarkcmd(UAContext *ua, TREE_CTX *tree)
567 rlsmark(ua, tree->node);
572 * This is actually the long form used for "dir"
574 static void ls_output(guid_list *guid, char *buf, const char *fname, const char *tag,
575 struct stat *statp, bool dot_cmd)
580 char en1[30], en2[30];
584 p = encode_mode(statp->st_mode, buf);
587 n = sprintf(p, "%d,", (uint32_t)statp->st_nlink);
589 n = sprintf(p, "%s,%s,",
590 guid->uid_to_name(statp->st_uid, en1, sizeof(en1)),
591 guid->gid_to_name(statp->st_gid, en2, sizeof(en2)));
593 n = sprintf(p, "%s,", edit_int64(statp->st_size, ec1));
595 p = encode_time(statp->st_mtime, p);
600 n = sprintf(p, " %2d ", (uint32_t)statp->st_nlink);
602 n = sprintf(p, "%-8.8s %-8.8s",
603 guid->uid_to_name(statp->st_uid, en1, sizeof(en1)),
604 guid->gid_to_name(statp->st_gid, en2, sizeof(en2)));
606 n = sprintf(p, "%12.12s ", edit_int64(statp->st_size, ec1));
608 if (statp->st_ctime > statp->st_mtime) {
609 time = statp->st_ctime;
611 time = statp->st_mtime;
613 /* Display most recent time */
614 p = encode_time(time, p);
618 for (f=fname; *f; ) {
625 * Like ls command, but give more detail on each file
627 static int do_dircmd(UAContext *ua, TREE_CTX *tree, bool dot_cmd)
633 char cwd[1100], *pcwd;
636 if (!tree_node_has_child(tree->node)) {
637 ua->send_msg(_("Node %s has no children.\n"), tree->node->fname);
641 guid = new_guid_list();
642 foreach_child(node, tree->node) {
644 if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) {
647 } else if (node->extract_dir) {
652 tree_getpath(node, cwd, sizeof(cwd));
654 fdbr.JobId = node->JobId;
656 * Strip / from soft links to directories.
657 * This is because soft links to files have a trailing slash
658 * when returned from tree_getpath, but db_get_file_attr...
659 * treats soft links as files, so they do not have a trailing
660 * slash like directory names.
662 if (node->type == TN_FILE && tree_node_has_child(node)) {
663 bstrncpy(buf, cwd, sizeof(buf));
665 int len = strlen(buf);
667 buf[len-1] = 0; /* strip trailing / */
672 if (db_get_file_attributes_record(ua->jcr, ua->db, pcwd, NULL, &fdbr)) {
674 decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
676 /* Something went wrong getting attributes -- print name */
677 memset(&statp, 0, sizeof(statp));
679 ls_output(guid, buf, cwd, tag, &statp, dot_cmd);
680 ua->send_msg("%s\n", buf);
683 free_guid_list(guid);
687 int dot_dircmd(UAContext *ua, TREE_CTX *tree)
689 return do_dircmd(ua, tree, true/*dot command*/);
692 static int dircmd(UAContext *ua, TREE_CTX *tree)
694 return do_dircmd(ua, tree, false/*not dot command*/);
698 static int estimatecmd(UAContext *ua, TREE_CTX *tree)
700 int total, num_extract;
701 uint64_t total_bytes = 0;
707 total = num_extract = 0;
708 for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
709 if (node->type != TN_NEWDIR) {
711 /* If regular file, get size */
712 if (node->extract && node->type == TN_FILE) {
714 tree_getpath(node, cwd, sizeof(cwd));
716 fdbr.JobId = node->JobId;
717 if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, NULL, &fdbr)) {
719 decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
720 if (S_ISREG(statp.st_mode) && statp.st_size > 0) {
721 total_bytes += statp.st_size;
724 /* Directory, count only */
725 } else if (node->extract || node->extract_dir) {
730 ua->send_msg(_("%d total files; %d marked to be restored; %s bytes.\n"),
731 total, num_extract, edit_uint64_with_commas(total_bytes, ec1));
737 static int helpcmd(UAContext *ua, TREE_CTX *tree)
741 ua->send_msg(_(" Command Description\n ======= ===========\n"));
742 for (i=0; i<comsize; i++) {
743 /* List only non-dot commands */
744 if (commands[i].key[0] != '.') {
745 ua->send_msg(" %-10s %s\n", _(commands[i].key), _(commands[i].help));
753 * Change directories. Note, if the user specifies x: and it fails,
754 * we assume it is a Win32 absolute cd rather than relative and
755 * try a second time with /x: ... Win32 kludge.
757 static int cdcmd(UAContext *ua, TREE_CTX *tree)
764 ua->error_msg(_("Too few or too many arguments. Try using double quotes.\n"));
767 node = tree_cwd(ua->argk[1], tree->root, tree->node);
769 /* Try once more if Win32 drive -- make absolute */
770 if (ua->argk[1][1] == ':') { /* win32 drive */
771 bstrncpy(cwd, "/", sizeof(cwd));
772 bstrncat(cwd, ua->argk[1], sizeof(cwd));
773 node = tree_cwd(cwd, tree->root, tree->node);
776 ua->warning_msg(_("Invalid path given.\n"));
783 return pwdcmd(ua, tree);
786 static int pwdcmd(UAContext *ua, TREE_CTX *tree)
789 tree_getpath(tree->node, cwd, sizeof(cwd));
791 ua->send_msg("%s", cwd);
793 ua->send_msg(_("cwd is: %s\n"), cwd);
798 static int dot_pwdcmd(UAContext *ua, TREE_CTX *tree)
801 tree_getpath(tree->node, cwd, sizeof(cwd));
802 ua->send_msg("%s", cwd);
806 static int unmarkcmd(UAContext *ua, TREE_CTX *tree)
811 if (ua->argc < 2 || !tree_node_has_child(tree->node)) {
812 ua->send_msg(_("No files unmarked.\n"));
815 for (int i=1; i < ua->argc; i++) {
816 strip_trailing_slash(ua->argk[i]);
817 foreach_child(node, tree->node) {
818 if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
819 count += set_extract(ua, node, tree, false);
824 ua->send_msg(_("No files unmarked.\n"));
825 } else if (count == 1) {
826 ua->send_msg(_("1 file unmarked.\n"));
829 ua->send_msg(_("%s files unmarked.\n"), edit_uint64_with_commas(count, ed1));
834 static int unmarkdircmd(UAContext *ua, TREE_CTX *tree)
839 if (ua->argc < 2 || !tree_node_has_child(tree->node)) {
840 ua->send_msg(_("No directories unmarked.\n"));
844 for (int i=1; i < ua->argc; i++) {
845 strip_trailing_slash(ua->argk[i]);
846 foreach_child(node, tree->node) {
847 if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
848 if (node->type == TN_DIR || node->type == TN_DIR_NLS) {
849 node->extract_dir = false;
857 ua->send_msg(_("No directories unmarked.\n"));
858 } else if (count == 1) {
859 ua->send_msg(_("1 directory unmarked.\n"));
861 ua->send_msg(_("%d directories unmarked.\n"), count);
867 static int donecmd(UAContext *ua, TREE_CTX *tree)
872 static int quitcmd(UAContext *ua, TREE_CTX *tree)