]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_tree.c
Add Peter Eriksson's const code + turn bsscanf code
[bacula/bacula] / bacula / src / dird / ua_tree.c
1 /*
2  *
3  *   Bacula Director -- User Agent Database File tree for Restore
4  *      command. This file interacts with the user implementing the
5  *      UA tree commands.
6  *
7  *     Kern Sibbald, July MMII
8  *
9  *   Version $Id$
10  */
11
12 /*
13    Copyright (C) 2002-2004 Kern Sibbald and John Walker
14
15    This program is free software; you can redistribute it and/or
16    modify it under the terms of the GNU General Public License as
17    published by the Free Software Foundation; either version 2 of
18    the License, or (at your option) any later version.
19
20    This program is distributed in the hope that it will be useful,
21    but WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23    General Public License for more details.
24
25    You should have received a copy of the GNU General Public
26    License along with this program; if not, write to the Free
27    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
28    MA 02111-1307, USA.
29
30  */
31
32 #include "bacula.h"
33 #include "dird.h"
34 #include <fnmatch.h>
35 #include "findlib/find.h"
36
37
38 /* Forward referenced commands */
39
40 static int markcmd(UAContext *ua, TREE_CTX *tree);
41 static int markdircmd(UAContext *ua, TREE_CTX *tree);
42 static int countcmd(UAContext *ua, TREE_CTX *tree);
43 static int findcmd(UAContext *ua, TREE_CTX *tree);
44 static int lscmd(UAContext *ua, TREE_CTX *tree);
45 static int lsmarkcmd(UAContext *ua, TREE_CTX *tree);
46 static int dircmd(UAContext *ua, TREE_CTX *tree);
47 static int estimatecmd(UAContext *ua, TREE_CTX *tree);
48 static int helpcmd(UAContext *ua, TREE_CTX *tree);
49 static int cdcmd(UAContext *ua, TREE_CTX *tree);
50 static int pwdcmd(UAContext *ua, TREE_CTX *tree);
51 static int unmarkcmd(UAContext *ua, TREE_CTX *tree);
52 static int unmarkdircmd(UAContext *ua, TREE_CTX *tree);
53 static int quitcmd(UAContext *ua, TREE_CTX *tree);
54 static int donecmd(UAContext *ua, TREE_CTX *tree);
55
56
57 struct cmdstruct { const char *key; int (*func)(UAContext *ua, TREE_CTX *tree); const char *help; }; 
58 static struct cmdstruct commands[] = {
59  { N_("cd"),         cdcmd,        _("change current directory")},
60  { N_("count"),      countcmd,     _("count marked files in and below the cd")},
61  { N_("dir"),        dircmd,       _("list current directory")},    
62  { N_("done"),       donecmd,      _("leave file selection mode")},
63  { N_("estimate"),   estimatecmd,  _("estimate restore size")},
64  { N_("exit"),       donecmd,      _("exit = done")},
65  { N_("find"),       findcmd,      _("find files -- wildcards allowed")},
66  { N_("help"),       helpcmd,      _("print help")},
67  { N_("ls"),         lscmd,        _("list current directory -- wildcards allowed")},    
68  { N_("lsmark"),     lsmarkcmd,    _("list the marked files in and below the cd")},    
69  { N_("mark"),       markcmd,      _("mark file to be restored")},
70  { N_("markdir"),    markdircmd,   _("mark directory entry to be restored -- nonrecursive")},
71  { N_("pwd"),        pwdcmd,       _("print current working directory")},
72  { N_("unmark"),     unmarkcmd,    _("unmark file to be restored")},
73  { N_("unmarkdir"),  unmarkdircmd, _("unmark directory -- no recursion")},
74  { N_("quit"),       quitcmd,      _("quit")},
75  { N_("?"),          helpcmd,      _("print help")},    
76              };
77 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
78
79
80 /*
81  * Enter a prompt mode where the user can select/deselect
82  *  files to be restored. This is sort of like a mini-shell
83  *  that allows "cd", "pwd", "add", "rm", ...
84  */
85 bool user_select_files_from_tree(TREE_CTX *tree)
86 {
87    char cwd[2000];
88    bool stat;
89    /* Get a new context so we don't destroy restore command args */
90    UAContext *ua = new_ua_context(tree->ua->jcr);
91    ua->UA_sock = tree->ua->UA_sock;   /* patch in UA socket */
92
93    bsendmsg(tree->ua, _( 
94       "\nYou are now entering file selection mode where you add and\n"
95       "remove files to be restored. All files are initially added.\n"
96       "Enter \"done\" to leave this mode.\n\n"));
97    /*
98     * Enter interactive command handler allowing selection
99     *  of individual files.
100     */
101    tree->node = (TREE_NODE *)tree->root;
102    tree_getpath(tree->node, cwd, sizeof(cwd));
103    bsendmsg(tree->ua, _("cwd is: %s\n"), cwd);
104    for ( ;; ) {       
105       int found, len, i;
106       if (!get_cmd(ua, "$ ")) {
107          break;
108       }
109       parse_ua_args(ua);
110       if (ua->argc == 0) {
111          break;
112       }
113
114       len = strlen(ua->argk[0]);
115       found = 0;
116       stat = false;
117       for (i=0; i<(int)comsize; i++)       /* search for command */
118          if (strncasecmp(ua->argk[0],  _(commands[i].key), len) == 0) {
119             stat = (*commands[i].func)(ua, tree);   /* go execute command */
120             found = 1;
121             break;
122          }
123       if (!found) {
124          bsendmsg(tree->ua, _("Illegal command. Enter \"done\" to exit.\n"));
125          continue;
126       }
127       if (!stat) {
128          break;
129       }
130    }
131    ua->UA_sock = NULL;                /* don't release restore socket */
132    stat = !ua->quit;
133    ua->quit = false;
134    free_ua_context(ua);               /* get rid of temp UA context */
135    return stat;
136 }
137
138
139 /*
140  * This callback routine is responsible for inserting the
141  *  items it gets into the directory tree. For each JobId selected
142  *  this routine is called once for each file. We do not allow
143  *  duplicate filenames, but instead keep the info from the most
144  *  recent file entered (i.e. the JobIds are assumed to be sorted)
145  *
146  *   See uar_sel_files in sql_cmds.c for query that calls us.
147  *      row[0]=Path, row[1]=Filename, row[2]=FileIndex
148  *      row[3]=JobId row[4]=LStat
149  */
150 int insert_tree_handler(void *ctx, int num_fields, char **row)
151 {
152    struct stat statp;
153    TREE_CTX *tree = (TREE_CTX *)ctx;
154    char fname[5000];
155    TREE_NODE *node, *new_node;
156    int type;
157    bool hard_link, first_time, ok;
158    int FileIndex;
159    JobId_t JobId;
160
161    strip_trailing_junk(row[1]);
162    if (*row[1] == 0) {                /* no filename => directory */
163       if (*row[0] != '/') {           /* Must be Win32 directory */
164          type = TN_DIR_NLS;
165       } else {
166          type = TN_DIR;
167       }
168    } else {
169       type = TN_FILE;
170    }
171    if (tree->avail_node) {
172       node = tree->avail_node;        /* if prev node avail use it */
173    } else {
174       node = new_tree_node(tree->root, type);  /* get new node */
175       tree->avail_node = node;
176    }
177    hard_link = (decode_LinkFI(row[4], &statp) != 0);
178    bsnprintf(fname, sizeof(fname), "%s%s%s", row[0], row[1], "");
179    Dmsg3(200, "FI=%d type=%d fname=%s\n", node->FileIndex, type, fname);
180    new_node = insert_tree_node(fname, node, tree->root, NULL);
181    /* Note, if node already exists, save new one for next time */
182    if (new_node != node) {
183       first_time = false;             /* we saw this file before */
184       tree->avail_node = node;        /* node already exists */
185    } else {
186       first_time = true;              /* first time we saw this file */
187       tree->avail_node = NULL;        /* added node to tree */
188    }
189    JobId = (JobId_t)str_to_int64(row[3]);
190    FileIndex = atoi(row[2]);
191    /*
192     * - The first time we see a file, we accept it.
193     * - In the same JobId, we accept only the first copy of a
194     *   hard linked file (the others are simply pointers).
195     * - In the same JobId, we accept the last copy of any other
196     *   file -- in particular directories.
197     *
198     * All the code to set ok could be condensed to a single
199     *  line, but it would be even harder to read.
200     */
201    ok = true;
202    if (!first_time && JobId == new_node->JobId) {
203       if ((hard_link && FileIndex > new_node->FileIndex) ||
204           (!hard_link && FileIndex < new_node->FileIndex)) {
205          ok = false;
206       }
207    }
208    if (ok) {
209       new_node->hard_link = hard_link;
210       new_node->FileIndex = FileIndex;
211       new_node->JobId = JobId;
212       new_node->type = type;
213       new_node->soft_link = S_ISLNK(statp.st_mode) != 0;
214       if (tree->all) {
215          new_node->extract = true;          /* extract all by default */
216          if (type == TN_DIR || type == TN_DIR_NLS) {
217             new_node->extract_dir = true;   /* if dir, extract it */
218          }
219       }
220    }
221    tree->cnt++;
222    return 0;
223 }
224
225
226 /*
227  * Set extract to value passed. We recursively walk
228  *  down the tree setting all children if the 
229  *  node is a directory.
230  */
231 static int set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, bool extract)
232 {
233    TREE_NODE *n;
234    FILE_DBR fdbr;
235    struct stat statp;
236    int count = 0;
237
238    node->extract = extract;
239    if (node->type == TN_DIR || node->type == TN_DIR_NLS) {
240       node->extract_dir = extract;    /* set/clear dir too */
241    }
242    if (node->type != TN_NEWDIR) {
243       count++;
244    }
245    /* For a non-file (i.e. directory), we see all the children */
246    if (node->type != TN_FILE || (node->soft_link && node->child)) {
247       /* Recursive set children within directory */
248       for (n=node->child; n; n=n->sibling) {
249          count += set_extract(ua, n, tree, extract);
250       }
251       /*
252        * Walk up tree marking any unextracted parent to be
253        * extracted.
254        */
255       if (extract) {
256          while (node->parent && !node->parent->extract_dir) {
257             node = node->parent;
258             node->extract_dir = true;
259          }
260       }
261    } else if (extract) {
262       char cwd[2000];
263       /*
264        * Ordinary file, we get the full path, look up the
265        * attributes, decode them, and if we are hard linked to
266        * a file that was saved, we must load that file too.
267        */
268       tree_getpath(node, cwd, sizeof(cwd));
269       fdbr.FileId = 0;
270       fdbr.JobId = node->JobId;
271       if (node->hard_link && db_get_file_attributes_record(ua->jcr, ua->db, cwd, NULL, &fdbr)) {
272          int32_t LinkFI;
273          decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
274          /*
275           * If we point to a hard linked file, traverse the tree to
276           * find that file, and mark it to be restored as well. It
277           * must have the Link we just obtained and the same JobId.
278           */
279          if (LinkFI) {
280             for (n=first_tree_node(tree->root); n; n=next_tree_node(n)) {
281                if (n->FileIndex == LinkFI && n->JobId == node->JobId) {
282                   n->extract = true;
283                   if (n->type == TN_DIR || n->type == TN_DIR_NLS) {
284                      n->extract_dir = true;
285                   }
286                   break;
287                }
288             }
289          }
290       }
291    }
292    return count;
293 }
294
295 /*
296  * Recursively mark the current directory to be restored as 
297  *  well as all directories and files below it.
298  */
299 static int markcmd(UAContext *ua, TREE_CTX *tree)
300 {
301    TREE_NODE *node;
302    int count = 0;
303
304    if (ua->argc < 2 || !tree->node->child) {
305       bsendmsg(ua, _("No files marked.\n"));
306       return 1;
307    }
308    for (int i=1; i < ua->argc; i++) {
309       for (node = tree->node->child; node; node=node->sibling) {
310          if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
311             count += set_extract(ua, node, tree, true);
312          }
313       }
314    }
315    if (count == 0) {
316       bsendmsg(ua, _("No files marked.\n"));
317    } else {
318       bsendmsg(ua, _("%d file%s marked.\n"), count, count==0?"":"s");
319    }
320    return 1;
321 }
322
323 static int markdircmd(UAContext *ua, TREE_CTX *tree)
324 {
325    TREE_NODE *node;
326    int count = 0;
327
328    if (ua->argc < 2 || !tree->node->child) {
329       bsendmsg(ua, _("No files marked.\n"));
330       return 1;
331    }
332    for (int i=1; i < ua->argc; i++) {
333       for (node = tree->node->child; node; node=node->sibling) {
334          if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
335             if (node->type == TN_DIR || node->type == TN_DIR_NLS) {
336                node->extract_dir = true;
337                count++;
338             }
339          }
340       }
341    }
342    if (count == 0) {
343       bsendmsg(ua, _("No directories marked.\n"));
344    } else {
345       bsendmsg(ua, _("%d director%s marked.\n"), count, count==1?"y":"ies");
346    }
347    return 1;
348 }
349
350
351 static int countcmd(UAContext *ua, TREE_CTX *tree)
352 {
353    int total, num_extract;
354
355    total = num_extract = 0;
356    for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
357       if (node->type != TN_NEWDIR) {
358          total++;
359          if (node->extract || node->extract_dir) {
360             num_extract++;
361          }
362       }
363    }
364    bsendmsg(ua, "%d total files/dirs. %d marked to be restored.\n", total, num_extract);
365    return 1;
366 }
367
368 static int findcmd(UAContext *ua, TREE_CTX *tree)
369 {
370    char cwd[2000];
371
372    if (ua->argc == 1) {
373       bsendmsg(ua, _("No file specification given.\n"));
374       return 0;
375    }
376    
377    for (int i=1; i < ua->argc; i++) {
378       for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
379          if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
380             const char *tag;
381             tree_getpath(node, cwd, sizeof(cwd));
382             if (node->extract) {
383                tag = "*";
384             } else if (node->extract_dir) {
385                tag = "+";
386             } else {
387                tag = "";
388             }
389             bsendmsg(ua, "%s%s\n", tag, cwd);
390          }
391       }
392    }
393    return 1;
394 }
395
396
397
398 static int lscmd(UAContext *ua, TREE_CTX *tree)
399 {
400    TREE_NODE *node;
401
402    if (!tree->node->child) {     
403       return 1;
404    }
405    for (node = tree->node->child; node; node=node->sibling) {
406       if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) {
407          const char *tag;
408          if (node->extract) {
409             tag = "*";
410          } else if (node->extract_dir) {
411             tag = "+";
412          } else {
413             tag = "";
414          }
415          bsendmsg(ua, "%s%s%s\n", tag, node->fname, node->child?"/":"");
416       }
417    }
418    return 1;
419 }
420
421 /*
422  * Ls command that lists only the marked files
423  */
424 static void rlsmark(UAContext *ua, TREE_NODE *node) 
425 {
426    if (!node->child) {     
427       return;
428    }
429    for (node = node->child; node; node=node->sibling) {
430       if ((ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) &&
431           (node->extract || node->extract_dir)) {
432          const char *tag;
433          if (node->extract) {
434             tag = "*";
435          } else if (node->extract_dir) {
436             tag = "+";
437          } else {
438             tag = "";
439          }
440          bsendmsg(ua, "%s%s%s\n", tag, node->fname, node->child?"/":"");
441          if (node->child) {
442             rlsmark(ua, node);
443          }
444       }
445    }
446 }
447
448 static int lsmarkcmd(UAContext *ua, TREE_CTX *tree)
449 {
450    rlsmark(ua, tree->node);
451    return 1;
452 }
453
454
455
456 extern char *getuser(uid_t uid);
457 extern char *getgroup(gid_t gid);
458
459 /*
460  * This is actually the long form used for "dir"
461  */
462 static void ls_output(char *buf, const char *fname, const char *tag, struct stat *statp)
463 {
464    char *p;
465    const char *f;
466    char ec1[30];
467    int n;
468
469    p = encode_mode(statp->st_mode, buf);
470    n = sprintf(p, "  %2d ", (uint32_t)statp->st_nlink);
471    p += n;
472    n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid));
473    p += n;
474    n = sprintf(p, "%8.8s  ", edit_uint64(statp->st_size, ec1));
475    p += n;
476    p = encode_time(statp->st_ctime, p);
477    *p++ = ' ';
478    *p++ = *tag;
479    for (f=fname; *f; ) {
480       *p++ = *f++;
481    }
482    *p = 0;
483 }
484
485
486 /*
487  * Like ls command, but give more detail on each file
488  */
489 static int dircmd(UAContext *ua, TREE_CTX *tree)
490 {
491    TREE_NODE *node;
492    FILE_DBR fdbr;
493    struct stat statp;
494    char buf[1100];
495    char cwd[1100], *pcwd;
496
497    if (!tree->node->child) {     
498       return 1;
499    }
500    for (node = tree->node->child; node; node=node->sibling) {
501       const char *tag;
502       if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) {
503          if (node->extract) {
504             tag = "*";
505          } else if (node->extract_dir) {
506             tag = "+";
507          } else {
508             tag = " ";
509          }
510          tree_getpath(node, cwd, sizeof(cwd));
511          fdbr.FileId = 0;
512          fdbr.JobId = node->JobId;
513          /*
514           * Strip / from soft links to directories.
515           *   This is because soft links to files have a trailing slash
516           *   when returned from tree_getpath, but db_get_file_attr...
517           *   treats soft links as files, so they do not have a trailing
518           *   slash like directory names.
519           */
520          if (node->type == TN_FILE && node->child) {
521             bstrncpy(buf, cwd, sizeof(buf));
522             pcwd = buf;
523             int len = strlen(buf);
524             if (len > 1) {
525                buf[len-1] = 0;        /* strip trailing / */
526             }
527          } else {
528             pcwd = cwd;
529          }
530          if (db_get_file_attributes_record(ua->jcr, ua->db, pcwd, NULL, &fdbr)) {
531             int32_t LinkFI;
532             decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
533          } else {
534             /* Something went wrong getting attributes -- print name */
535             memset(&statp, 0, sizeof(statp));
536          }
537          ls_output(buf, cwd, tag, &statp);
538          bsendmsg(ua, "%s\n", buf);
539       }
540    }
541    return 1;
542 }
543
544
545 static int estimatecmd(UAContext *ua, TREE_CTX *tree)
546 {
547    int total, num_extract;
548    uint64_t total_bytes = 0;
549    FILE_DBR fdbr;
550    struct stat statp;
551    char cwd[1100];
552    char ec1[50];
553
554    total = num_extract = 0;
555    for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
556       if (node->type != TN_NEWDIR) {
557          total++;
558          /* If regular file, get size */
559          if (node->extract && node->type == TN_FILE) {
560             num_extract++;
561             tree_getpath(node, cwd, sizeof(cwd));
562             fdbr.FileId = 0;
563             fdbr.JobId = node->JobId;
564             if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, NULL, &fdbr)) {
565                int32_t LinkFI;
566                decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
567                if (S_ISREG(statp.st_mode) && statp.st_size > 0) {
568                   total_bytes += statp.st_size;
569                }
570             }
571          /* Directory, count only */
572          } else if (node->extract || node->extract_dir) {
573             num_extract++;
574          }
575       }
576    }
577    bsendmsg(ua, "%d total files; %d marked to be restored; %s bytes.\n", 
578             total, num_extract, edit_uint64_with_commas(total_bytes, ec1));
579    return 1;
580 }
581
582
583
584 static int helpcmd(UAContext *ua, TREE_CTX *tree) 
585 {
586    unsigned int i;
587
588 /* usage(); */
589    bsendmsg(ua, _("  Command    Description\n  =======    ===========\n"));
590    for (i=0; i<comsize; i++) {
591       bsendmsg(ua, _("  %-10s %s\n"), _(commands[i].key), _(commands[i].help));
592    }
593    bsendmsg(ua, "\n");
594    return 1;
595 }
596
597 /*
598  * Change directories.  Note, if the user specifies x: and it fails,
599  *   we assume it is a Win32 absolute cd rather than relative and
600  *   try a second time with /x: ...  Win32 kludge.
601  */
602 static int cdcmd(UAContext *ua, TREE_CTX *tree) 
603 {
604    TREE_NODE *node;
605    char cwd[2000];
606
607    if (ua->argc != 2) {
608       return 1;
609    }
610    node = tree_cwd(ua->argk[1], tree->root, tree->node);
611    if (!node) {
612       /* Try once more if Win32 drive -- make absolute */
613       if (ua->argk[1][1] == ':') {  /* win32 drive */
614          bstrncpy(cwd, "/", sizeof(cwd));
615          bstrncat(cwd, ua->argk[1], sizeof(cwd));
616          node = tree_cwd(cwd, tree->root, tree->node);
617       }
618       if (!node) {
619          bsendmsg(ua, _("Invalid path given.\n"));
620       } else {
621          tree->node = node;
622       }
623    } else {
624       tree->node = node;
625    }
626    tree_getpath(tree->node, cwd, sizeof(cwd));
627    bsendmsg(ua, _("cwd is: %s\n"), cwd);
628    return 1;
629 }
630
631 static int pwdcmd(UAContext *ua, TREE_CTX *tree) 
632 {
633    char cwd[2000];
634    tree_getpath(tree->node, cwd, sizeof(cwd));
635    bsendmsg(ua, _("cwd is: %s\n"), cwd);
636    return 1;
637 }
638
639
640 static int unmarkcmd(UAContext *ua, TREE_CTX *tree)
641 {
642    TREE_NODE *node;
643    int count = 0;
644
645    if (ua->argc < 2 || !tree->node->child) {     
646       bsendmsg(ua, _("No files unmarked.\n"));
647       return 1;
648    }
649    for (int i=1; i < ua->argc; i++) {
650       for (node = tree->node->child; node; node=node->sibling) {
651          if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
652             count += set_extract(ua, node, tree, false);
653          }
654       }
655    }
656    if (count == 0) {
657       bsendmsg(ua, _("No files unmarked.\n"));
658    } else {
659       bsendmsg(ua, _("%d file%s unmarked.\n"), count, count==0?"":"s");
660    }
661    return 1;
662 }
663
664 static int unmarkdircmd(UAContext *ua, TREE_CTX *tree)
665 {
666    TREE_NODE *node;
667    int count = 0;
668
669    if (ua->argc < 2 || !tree->node->child) {
670       bsendmsg(ua, _("No directories unmarked.\n"));
671       return 1;
672    }
673
674    for (int i=1; i < ua->argc; i++) {
675       for (node = tree->node->child; node; node=node->sibling) {
676          if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
677             if (node->type == TN_DIR || node->type == TN_DIR_NLS) {
678                node->extract_dir = false;
679                count++;
680             }
681          }
682       }
683    }
684
685    if (count == 0) {
686       bsendmsg(ua, _("No directories unmarked.\n"));
687    } else {
688       bsendmsg(ua, _("%d director%s unmarked.\n"), count, count==1?"y":"ies");
689    }
690    return 1;
691 }
692
693
694 static int donecmd(UAContext *ua, TREE_CTX *tree) 
695 {
696    return 0;
697 }
698
699 static int quitcmd(UAContext *ua, TREE_CTX *tree) 
700 {
701    ua->quit = true;
702    return 0;
703 }