2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2007 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 two of the GNU General Public
10 License as published by the Free Software Foundation plus additions
11 that are listed in the file LICENSE.
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 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 John Walker.
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
44 #include "lib/fnmatch.h"
46 #include "findlib/find.h"
49 /* Forward referenced commands */
51 static int markcmd(UAContext *ua, TREE_CTX *tree);
52 static int markdircmd(UAContext *ua, TREE_CTX *tree);
53 static int countcmd(UAContext *ua, TREE_CTX *tree);
54 static int findcmd(UAContext *ua, TREE_CTX *tree);
55 static int lscmd(UAContext *ua, TREE_CTX *tree);
56 static int lsmarkcmd(UAContext *ua, TREE_CTX *tree);
57 static int dircmd(UAContext *ua, TREE_CTX *tree);
58 static int dot_dircmd(UAContext *ua, TREE_CTX *tree);
59 static int estimatecmd(UAContext *ua, TREE_CTX *tree);
60 static int helpcmd(UAContext *ua, TREE_CTX *tree);
61 static int cdcmd(UAContext *ua, TREE_CTX *tree);
62 static int pwdcmd(UAContext *ua, TREE_CTX *tree);
63 static int dot_pwdcmd(UAContext *ua, TREE_CTX *tree);
64 static int unmarkcmd(UAContext *ua, TREE_CTX *tree);
65 static int unmarkdircmd(UAContext *ua, TREE_CTX *tree);
66 static int quitcmd(UAContext *ua, TREE_CTX *tree);
67 static int donecmd(UAContext *ua, TREE_CTX *tree);
70 struct cmdstruct { const char *key; int (*func)(UAContext *ua, TREE_CTX *tree); const char *help; };
71 static struct cmdstruct commands[] = {
72 { NT_("cd"), cdcmd, _("change current directory")},
73 { NT_("count"), countcmd, _("count marked files in and below the cd")},
74 { NT_("dir"), dircmd, _("long list current directory, wildcards allowed")},
75 { NT_(".dir"), dot_dircmd, _("long list current directory, wildcards allowed")},
76 { NT_("done"), donecmd, _("leave file selection mode")},
77 { NT_("estimate"), estimatecmd, _("estimate restore size")},
78 { NT_("exit"), donecmd, _("same as done command")},
79 { NT_("find"), findcmd, _("find files, wildcards allowed")},
80 { NT_("help"), helpcmd, _("print help")},
81 { NT_("ls"), lscmd, _("list current directory, wildcards allowed")},
82 { NT_("lsmark"), lsmarkcmd, _("list the marked files in and below the cd")},
83 { NT_("mark"), markcmd, _("mark dir/file to be restored recursively, wildcards allowed")},
84 { NT_("markdir"), markdircmd, _("mark directory name to be restored (no files)")},
85 { NT_("pwd"), pwdcmd, _("print current working directory")},
86 { NT_(".pwd"), dot_pwdcmd, _("print current working directory")},
87 { NT_("unmark"), unmarkcmd, _("unmark dir/file to be restored recursively in dir")},
88 { NT_("unmarkdir"), unmarkdircmd, _("unmark directory name only no recursion")},
89 { NT_("quit"), quitcmd, _("quit and do not do restore")},
90 { NT_("?"), helpcmd, _("print help")},
92 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
95 * Enter a prompt mode where the user can select/deselect
96 * files to be restored. This is sort of like a mini-shell
97 * that allows "cd", "pwd", "add", "rm", ...
99 bool user_select_files_from_tree(TREE_CTX *tree)
103 /* Get a new context so we don't destroy restore command args */
104 UAContext *ua = new_ua_context(tree->ua->jcr);
105 ua->UA_sock = tree->ua->UA_sock; /* patch in UA socket */
106 BSOCK *user = ua->UA_sock;
108 bsendmsg(tree->ua, _(
109 "\nYou are now entering file selection mode where you add (mark) and\n"
110 "remove (unmark) files to be restored. No files are initially added, unless\n"
111 "you used the \"all\" keyword on the command line.\n"
112 "Enter \"done\" to leave this mode.\n\n"));
114 * Enter interactive command handler allowing selection
115 * of individual files.
117 tree->node = (TREE_NODE *)tree->root;
118 tree_getpath(tree->node, cwd, sizeof(cwd));
119 bsendmsg(tree->ua, _("cwd is: %s\n"), cwd);
122 if (!get_cmd(ua, "$ ")) {
125 if (ua->api) user->signal(BNET_CMD_BEGIN);
126 parse_args_only(ua->cmd, &ua->args, &ua->argc, ua->argk, ua->argv, MAX_CMD_ARGS);
128 bsendmsg(tree->ua, _("Invalid command. Enter \"done\" to exit.\n"));
129 if (ua->api) user->signal(BNET_CMD_FAILED);
133 len = strlen(ua->argk[0]);
136 for (i=0; i<comsize; i++) /* search for command */
137 if (strncasecmp(ua->argk[0], _(commands[i].key), len) == 0) {
138 stat = (*commands[i].func)(ua, tree); /* go execute command */
143 bsendmsg(tree->ua, _("Invalid command. Enter \"done\" to exit.\n"));
144 if (ua->api) user->signal(BNET_CMD_FAILED);
147 if (ua->api) user->signal(BNET_CMD_OK);
152 ua->UA_sock = NULL; /* don't release restore socket */
155 free_ua_context(ua); /* get rid of temp UA context */
161 * This callback routine is responsible for inserting the
162 * items it gets into the directory tree. For each JobId selected
163 * this routine is called once for each file. We do not allow
164 * duplicate filenames, but instead keep the info from the most
165 * recent file entered (i.e. the JobIds are assumed to be sorted)
167 * See uar_sel_files in sql_cmds.c for query that calls us.
168 * row[0]=Path, row[1]=Filename, row[2]=FileIndex
169 * row[3]=JobId row[4]=LStat
171 int insert_tree_handler(void *ctx, int num_fields, char **row)
174 TREE_CTX *tree = (TREE_CTX *)ctx;
181 // Dmsg4(000, "Path=%s%s FI=%s JobId=%s\n", row[0], row[1],
183 if (*row[1] == 0) { /* no filename => directory */
184 if (!IsPathSeparator(*row[0])) { /* Must be Win32 directory */
192 hard_link = (decode_LinkFI(row[4], &statp) != 0);
193 node = insert_tree_node(row[0], row[1], type, tree->root, NULL);
194 JobId = str_to_int64(row[3]);
195 FileIndex = str_to_int64(row[2]);
197 * - The first time we see a file (node->inserted==true), we accept it.
198 * - In the same JobId, we accept only the first copy of a
199 * hard linked file (the others are simply pointers).
200 * - In the same JobId, we accept the last copy of any other
201 * file -- in particular directories.
203 * All the code to set ok could be condensed to a single
204 * line, but it would be even harder to read.
207 if (!node->inserted && JobId == node->JobId) {
208 if ((hard_link && FileIndex > node->FileIndex) ||
209 (!hard_link && FileIndex < node->FileIndex)) {
214 node->hard_link = hard_link;
215 node->FileIndex = FileIndex;
218 node->soft_link = S_ISLNK(statp.st_mode) != 0;
220 node->extract = true; /* extract all by default */
221 if (type == TN_DIR || type == TN_DIR_NLS) {
222 node->extract_dir = true; /* if dir, extract it */
226 if (node->inserted) {
228 if (tree->DeltaCount > 0 && (tree->FileCount-tree->LastCount) > tree->DeltaCount) {
229 bsendmsg(tree->ua, "+");
230 tree->LastCount = tree->FileCount;
239 * Set extract to value passed. We recursively walk
240 * down the tree setting all children if the
241 * node is a directory.
243 static int set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, bool extract)
250 node->extract = extract;
251 if (node->type == TN_DIR || node->type == TN_DIR_NLS) {
252 node->extract_dir = extract; /* set/clear dir too */
254 if (node->type != TN_NEWDIR) {
257 /* For a non-file (i.e. directory), we see all the children */
258 if (node->type != TN_FILE || (node->soft_link && tree_node_has_child(node))) {
259 /* Recursive set children within directory */
260 foreach_child(n, node) {
261 count += set_extract(ua, n, tree, extract);
264 * Walk up tree marking any unextracted parent to be
268 while (node->parent && !node->parent->extract_dir) {
270 node->extract_dir = true;
273 } else if (extract) {
276 * Ordinary file, we get the full path, look up the
277 * attributes, decode them, and if we are hard linked to
278 * a file that was saved, we must load that file too.
280 tree_getpath(node, cwd, sizeof(cwd));
282 fdbr.JobId = node->JobId;
283 if (node->hard_link && db_get_file_attributes_record(ua->jcr, ua->db, cwd, NULL, &fdbr)) {
285 decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
287 * If we point to a hard linked file, traverse the tree to
288 * find that file, and mark it to be restored as well. It
289 * must have the Link we just obtained and the same JobId.
292 for (n=first_tree_node(tree->root); n; n=next_tree_node(n)) {
293 if (n->FileIndex == LinkFI && n->JobId == node->JobId) {
295 if (n->type == TN_DIR || n->type == TN_DIR_NLS) {
296 n->extract_dir = true;
307 static void strip_trailing_slash(char *arg)
309 int len = strlen(arg);
314 if (arg[len] == '/') { /* strip any trailing slash */
320 * Recursively mark the current directory to be restored as
321 * well as all directories and files below it.
323 static int markcmd(UAContext *ua, TREE_CTX *tree)
329 if (ua->argc < 2 || !tree_node_has_child(tree->node)) {
330 bsendmsg(ua, _("No files marked.\n"));
333 for (int i=1; i < ua->argc; i++) {
334 strip_trailing_slash(ua->argk[i]);
335 foreach_child(node, tree->node) {
336 if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
337 count += set_extract(ua, node, tree, true);
342 bsendmsg(ua, _("No files marked.\n"));
343 } else if (count == 1) {
344 bsendmsg(ua, _("1 file marked.\n"));
346 bsendmsg(ua, _("%s files marked.\n"),
347 edit_uint64_with_commas(count, ec1));
352 static int markdircmd(UAContext *ua, TREE_CTX *tree)
358 if (ua->argc < 2 || !tree_node_has_child(tree->node)) {
359 bsendmsg(ua, _("No files marked.\n"));
362 for (int i=1; i < ua->argc; i++) {
363 strip_trailing_slash(ua->argk[i]);
364 foreach_child(node, tree->node) {
365 if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
366 if (node->type == TN_DIR || node->type == TN_DIR_NLS) {
367 node->extract_dir = true;
374 bsendmsg(ua, _("No directories marked.\n"));
375 } else if (count == 1) {
376 bsendmsg(ua, _("1 directory marked.\n"));
378 bsendmsg(ua, _("%s directories marked.\n"),
379 edit_uint64_with_commas(count, ec1));
385 static int countcmd(UAContext *ua, TREE_CTX *tree)
387 int total, num_extract;
388 char ec1[50], ec2[50];
390 total = num_extract = 0;
391 for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
392 if (node->type != TN_NEWDIR) {
394 if (node->extract || node->extract_dir) {
399 bsendmsg(ua, _("%s total files/dirs. %s marked to be restored.\n"),
400 edit_uint64_with_commas(total, ec1),
401 edit_uint64_with_commas(num_extract, ec2));
405 static int findcmd(UAContext *ua, TREE_CTX *tree)
410 bsendmsg(ua, _("No file specification given.\n"));
411 return 1; /* make it non-fatal */
414 for (int i=1; i < ua->argc; i++) {
415 for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
416 if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
418 tree_getpath(node, cwd, sizeof(cwd));
421 } else if (node->extract_dir) {
426 bsendmsg(ua, "%s%s\n", tag, cwd);
435 static int lscmd(UAContext *ua, TREE_CTX *tree)
439 if (!tree_node_has_child(tree->node)) {
442 foreach_child(node, tree->node) {
443 if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) {
447 } else if (node->extract_dir) {
452 bsendmsg(ua, "%s%s%s\n", tag, node->fname, tree_node_has_child(node)?"/":"");
459 * Ls command that lists only the marked files
461 static void rlsmark(UAContext *ua, TREE_NODE *tnode)
464 if (!tree_node_has_child(tnode)) {
467 foreach_child(node, tnode) {
468 if ((ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) &&
469 (node->extract || node->extract_dir)) {
473 } else if (node->extract_dir) {
478 bsendmsg(ua, "%s%s%s\n", tag, node->fname, tree_node_has_child(node)?"/":"");
479 if (tree_node_has_child(node)) {
486 static int lsmarkcmd(UAContext *ua, TREE_CTX *tree)
488 rlsmark(ua, tree->node);
494 extern char *getuser(uid_t uid, char *name, int len);
495 extern char *getgroup(gid_t gid, char *name, int len);
498 * This is actually the long form used for "dir"
500 static void ls_output(char *buf, const char *fname, const char *tag,
501 struct stat *statp, bool dot_cmd)
507 char en1[30], en2[30];
511 p = encode_mode(statp->st_mode, buf);
514 n = sprintf(p, "%d,", (uint32_t)statp->st_nlink);
516 n = sprintf(p, "%s,%s,", getuser(statp->st_uid, en1, sizeof(en1)),
517 getgroup(statp->st_gid, en2, sizeof(en2)));
519 n = sprintf(p, "%s,", edit_uint64(statp->st_size, ec1));
521 p = encode_time(statp->st_mtime, p);
526 n = sprintf(p, " %2d ", (uint32_t)statp->st_nlink);
528 n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid, en1, sizeof(en1)),
529 getgroup(statp->st_gid, en2, sizeof(en2)));
531 n = sprintf(p, "%10.10s ", edit_uint64(statp->st_size, ec1));
533 if (statp->st_ctime > statp->st_mtime) {
534 time = statp->st_ctime;
536 time = statp->st_mtime;
538 /* Display most recent time */
539 p = encode_time(time, p);
543 for (f=fname; *f; ) {
550 * Like ls command, but give more detail on each file
552 static int do_dircmd(UAContext *ua, TREE_CTX *tree, bool dot_cmd)
558 char cwd[1100], *pcwd;
560 if (!tree_node_has_child(tree->node)) {
561 bsendmsg(ua, _("Node %s has no children.\n"), tree->node->fname);
565 foreach_child(node, tree->node) {
567 if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) {
570 } else if (node->extract_dir) {
575 tree_getpath(node, cwd, sizeof(cwd));
577 fdbr.JobId = node->JobId;
579 * Strip / from soft links to directories.
580 * This is because soft links to files have a trailing slash
581 * when returned from tree_getpath, but db_get_file_attr...
582 * treats soft links as files, so they do not have a trailing
583 * slash like directory names.
585 if (node->type == TN_FILE && tree_node_has_child(node)) {
586 bstrncpy(buf, cwd, sizeof(buf));
588 int len = strlen(buf);
590 buf[len-1] = 0; /* strip trailing / */
595 if (db_get_file_attributes_record(ua->jcr, ua->db, pcwd, NULL, &fdbr)) {
597 decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
599 /* Something went wrong getting attributes -- print name */
600 memset(&statp, 0, sizeof(statp));
602 ls_output(buf, cwd, tag, &statp, dot_cmd);
603 bsendmsg(ua, "%s\n", buf);
609 int dot_dircmd(UAContext *ua, TREE_CTX *tree)
611 return do_dircmd(ua, tree, true/*dot command*/);
614 static int dircmd(UAContext *ua, TREE_CTX *tree)
616 return do_dircmd(ua, tree, false/*not dot command*/);
620 static int estimatecmd(UAContext *ua, TREE_CTX *tree)
622 int total, num_extract;
623 uint64_t total_bytes = 0;
629 total = num_extract = 0;
630 for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
631 if (node->type != TN_NEWDIR) {
633 /* If regular file, get size */
634 if (node->extract && node->type == TN_FILE) {
636 tree_getpath(node, cwd, sizeof(cwd));
638 fdbr.JobId = node->JobId;
639 if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, NULL, &fdbr)) {
641 decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
642 if (S_ISREG(statp.st_mode) && statp.st_size > 0) {
643 total_bytes += statp.st_size;
646 /* Directory, count only */
647 } else if (node->extract || node->extract_dir) {
652 bsendmsg(ua, _("%d total files; %d marked to be restored; %s bytes.\n"),
653 total, num_extract, edit_uint64_with_commas(total_bytes, ec1));
659 static int helpcmd(UAContext *ua, TREE_CTX *tree)
663 bsendmsg(ua, _(" Command Description\n ======= ===========\n"));
664 for (i=0; i<comsize; i++) {
665 /* List only non-dot commands */
666 if (commands[i].key[0] != '.') {
667 bsendmsg(ua, " %-10s %s\n", _(commands[i].key), _(commands[i].help));
675 * Change directories. Note, if the user specifies x: and it fails,
676 * we assume it is a Win32 absolute cd rather than relative and
677 * try a second time with /x: ... Win32 kludge.
679 static int cdcmd(UAContext *ua, TREE_CTX *tree)
686 bsendmsg(ua, _("Too few or too many arguments. Try using double quotes.\n"));
689 node = tree_cwd(ua->argk[1], tree->root, tree->node);
691 /* Try once more if Win32 drive -- make absolute */
692 if (ua->argk[1][1] == ':') { /* win32 drive */
693 bstrncpy(cwd, "/", sizeof(cwd));
694 bstrncat(cwd, ua->argk[1], sizeof(cwd));
695 node = tree_cwd(cwd, tree->root, tree->node);
698 bsendmsg(ua, _("Invalid path given.\n"));
705 tree_getpath(tree->node, cwd, sizeof(cwd));
707 bsendmsg(ua, "%s", cwd);
709 bsendmsg(ua, _("cwd is: %s\n"), cwd);
714 static int pwdcmd(UAContext *ua, TREE_CTX *tree)
717 tree_getpath(tree->node, cwd, sizeof(cwd));
718 bsendmsg(ua, _("cwd is: %s\n"), cwd);
722 static int dot_pwdcmd(UAContext *ua, TREE_CTX *tree)
725 tree_getpath(tree->node, cwd, sizeof(cwd));
726 bsendmsg(ua, _("%s"), cwd);
730 static int unmarkcmd(UAContext *ua, TREE_CTX *tree)
735 if (ua->argc < 2 || !tree_node_has_child(tree->node)) {
736 bsendmsg(ua, _("No files unmarked.\n"));
739 for (int i=1; i < ua->argc; i++) {
740 strip_trailing_slash(ua->argk[i]);
741 foreach_child(node, tree->node) {
742 if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
743 count += set_extract(ua, node, tree, false);
748 bsendmsg(ua, _("No files unmarked.\n"));
749 } else if (count == 1) {
750 bsendmsg(ua, _("1 file unmarked.\n"));
753 bsendmsg(ua, _("%s files unmarked.\n"), edit_uint64_with_commas(count, ed1));
758 static int unmarkdircmd(UAContext *ua, TREE_CTX *tree)
763 if (ua->argc < 2 || !tree_node_has_child(tree->node)) {
764 bsendmsg(ua, _("No directories unmarked.\n"));
768 for (int i=1; i < ua->argc; i++) {
769 strip_trailing_slash(ua->argk[i]);
770 foreach_child(node, tree->node) {
771 if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
772 if (node->type == TN_DIR || node->type == TN_DIR_NLS) {
773 node->extract_dir = false;
781 bsendmsg(ua, _("No directories unmarked.\n"));
782 } else if (count == 1) {
783 bsendmsg(ua, _("1 directory unmarked.\n"));
785 bsendmsg(ua, _("%d directories unmarked.\n"), count);
791 static int donecmd(UAContext *ua, TREE_CTX *tree)
796 static int quitcmd(UAContext *ua, TREE_CTX *tree)