X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fdird%2Fua_tree.c;h=d818494af608de28ca9241ce8d9bd325f5cffb80;hb=refs%2Ftags%2FRelease-1.38.11;hp=7af685a6c1ec931094e150151731bba19a5715c1;hpb=d8d6b1553da4e6e101e95112458c72da39b8381a;p=bacula%2Fbacula diff --git a/bacula/src/dird/ua_tree.c b/bacula/src/dird/ua_tree.c index 7af685a6c1..d818494af6 100644 --- a/bacula/src/dird/ua_tree.c +++ b/bacula/src/dird/ua_tree.c @@ -1,37 +1,35 @@ /* * * Bacula Director -- User Agent Database File tree for Restore - * command. This file interacts with the user implementing the - * UA tree commands. + * command. This file interacts with the user implementing the + * UA tree commands. * * Kern Sibbald, July MMII * * Version $Id$ */ - /* - Copyright (C) 2002-2004 Kern Sibbald and John Walker + Copyright (C) 2002-2005 Kern Sibbald This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. + modify it under the terms of the GNU General Public License + version 2 as amended with additional clauses defined in the + file LICENSE in the main source directory. 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., 59 Temple Place - Suite 330, Boston, - MA 02111-1307, USA. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + the file LICENSE for additional details. */ #include "bacula.h" #include "dird.h" +#ifdef HAVE_FNMATCH #include +#else +#include "lib/fnmatch.h" +#endif #include "findlib/find.h" @@ -44,6 +42,7 @@ static int findcmd(UAContext *ua, TREE_CTX *tree); static int lscmd(UAContext *ua, TREE_CTX *tree); static int lsmarkcmd(UAContext *ua, TREE_CTX *tree); static int dircmd(UAContext *ua, TREE_CTX *tree); +static int dot_dircmd(UAContext *ua, TREE_CTX *tree); static int estimatecmd(UAContext *ua, TREE_CTX *tree); static int helpcmd(UAContext *ua, TREE_CTX *tree); static int cdcmd(UAContext *ua, TREE_CTX *tree); @@ -54,26 +53,27 @@ static int quitcmd(UAContext *ua, TREE_CTX *tree); static int donecmd(UAContext *ua, TREE_CTX *tree); -struct cmdstruct { char *key; int (*func)(UAContext *ua, TREE_CTX *tree); char *help; }; +struct cmdstruct { const char *key; int (*func)(UAContext *ua, TREE_CTX *tree); const char *help; }; static struct cmdstruct commands[] = { - { N_("cd"), cdcmd, _("change current directory")}, - { N_("count"), countcmd, _("count marked files in and below the cd")}, - { N_("dir"), dircmd, _("list current directory")}, - { N_("done"), donecmd, _("leave file selection mode")}, - { N_("estimate"), estimatecmd, _("estimate restore size")}, - { N_("exit"), donecmd, _("exit = done")}, - { N_("find"), findcmd, _("find files -- wildcards allowed")}, - { N_("help"), helpcmd, _("print help")}, - { N_("ls"), lscmd, _("list current directory -- wildcards allowed")}, - { N_("lsmark"), lsmarkcmd, _("list the marked files in and below the cd")}, - { N_("mark"), markcmd, _("mark file to be restored")}, - { N_("markdir"), markdircmd, _("mark directory entry to be restored -- nonrecursive")}, - { N_("pwd"), pwdcmd, _("print current working directory")}, - { N_("unmark"), unmarkcmd, _("unmark file to be restored")}, - { N_("unmarkdir"), unmarkdircmd, _("unmark directory -- no recursion")}, - { N_("quit"), quitcmd, _("quit")}, - { N_("?"), helpcmd, _("print help")}, - }; + { NT_("cd"), cdcmd, _("change current directory")}, + { NT_("count"), countcmd, _("count marked files in and below the cd")}, + { NT_("dir"), dircmd, _("long list current directory, wildcards allowed")}, + { NT_(".dir"), dot_dircmd, _("long list current directory, wildcards allowed")}, + { NT_("done"), donecmd, _("leave file selection mode")}, + { NT_("estimate"), estimatecmd, _("estimate restore size")}, + { NT_("exit"), donecmd, _("same as done command")}, + { NT_("find"), findcmd, _("find files, wildcards allowed")}, + { NT_("help"), helpcmd, _("print help")}, + { NT_("ls"), lscmd, _("list current directory, wildcards allowed")}, + { NT_("lsmark"), lsmarkcmd, _("list the marked files in and below the cd")}, + { 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")}, + { 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_("?"), helpcmd, _("print help")}, + }; #define comsize (sizeof(commands)/sizeof(struct cmdstruct)) @@ -90,9 +90,10 @@ bool user_select_files_from_tree(TREE_CTX *tree) UAContext *ua = new_ua_context(tree->ua->jcr); ua->UA_sock = tree->ua->UA_sock; /* patch in UA socket */ - bsendmsg(tree->ua, _( - "\nYou are now entering file selection mode where you add and\n" - "remove files to be restored. All files are initially added.\n" + bsendmsg(tree->ua, _( + "\nYou are now entering file selection mode where you add (mark) and\n" + "remove (unmark) files to be restored. No files are initially added, unless\n" + "you used the \"all\" keyword on the command line.\n" "Enter \"done\" to leave this mode.\n\n")); /* * Enter interactive command handler allowing selection @@ -101,37 +102,37 @@ bool user_select_files_from_tree(TREE_CTX *tree) tree->node = (TREE_NODE *)tree->root; tree_getpath(tree->node, cwd, sizeof(cwd)); bsendmsg(tree->ua, _("cwd is: %s\n"), cwd); - for ( ;; ) { + for ( ;; ) { int found, len, i; if (!get_cmd(ua, "$ ")) { - break; + break; } parse_ua_args(ua); if (ua->argc == 0) { - break; + break; } len = strlen(ua->argk[0]); found = 0; stat = false; - for (i=0; i<(int)comsize; i++) /* search for command */ - if (strncasecmp(ua->argk[0], _(commands[i].key), len) == 0) { - stat = (*commands[i].func)(ua, tree); /* go execute command */ - found = 1; - break; - } + for (i=0; i<(int)comsize; i++) /* search for command */ + if (strncasecmp(ua->argk[0], _(commands[i].key), len) == 0) { + stat = (*commands[i].func)(ua, tree); /* go execute command */ + found = 1; + break; + } if (!found) { bsendmsg(tree->ua, _("Illegal command. Enter \"done\" to exit.\n")); - continue; + continue; } if (!stat) { - break; + break; } } ua->UA_sock = NULL; /* don't release restore socket */ stat = !ua->quit; ua->quit = false; - free_ua_context(ua); /* get rid of temp UA context */ + free_ua_context(ua); /* get rid of temp UA context */ return stat; } @@ -144,59 +145,69 @@ bool user_select_files_from_tree(TREE_CTX *tree) * recent file entered (i.e. the JobIds are assumed to be sorted) * * 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[0]=Path, row[1]=Filename, row[2]=FileIndex + * row[3]=JobId row[4]=LStat */ int insert_tree_handler(void *ctx, int num_fields, char **row) { struct stat statp; TREE_CTX *tree = (TREE_CTX *)ctx; - char fname[5000]; - TREE_NODE *node, *new_node; + TREE_NODE *node; int type; - bool hard_link; + bool hard_link, ok; + int FileIndex; + JobId_t JobId; - strip_trailing_junk(row[1]); - if (*row[1] == 0) { /* no filename => directory */ +// Dmsg4(000, "Path=%s%s FI=%s JobId=%s\n", row[0], row[1], +// row[2], row[3]); + if (*row[1] == 0) { /* no filename => directory */ if (*row[0] != '/') { /* Must be Win32 directory */ - type = TN_DIR_NLS; + type = TN_DIR_NLS; } else { - type = TN_DIR; + type = TN_DIR; } } else { type = TN_FILE; } - if (tree->avail_node) { - node = tree->avail_node; /* if prev node avail use it */ - } else { - node = new_tree_node(tree->root, type); /* get new node */ - tree->avail_node = node; - } hard_link = (decode_LinkFI(row[4], &statp) != 0); - bsnprintf(fname, sizeof(fname), "%s%s%s", row[0], row[1], ""); -// S_ISLNK(statp.st_mode)?" ->":""); - Dmsg3(200, "FI=%d type=%d fname=%s\n", node->FileIndex, type, fname); - new_node = insert_tree_node(fname, node, tree->root, NULL); - /* Note, if node already exists, save new one for next time */ - if (new_node != node) { - tree->avail_node = node; /* node already exists */ - } else { - tree->avail_node = NULL; /* added node to tree */ - } + node = insert_tree_node(row[0], row[1], type, tree->root, NULL); + JobId = str_to_int64(row[3]); + FileIndex = str_to_int64(row[2]); /* - * If the user has backed up a hard linked file twice, the - * second copy will be a pointer (i.e. hard link will be set), - * so we do not overwrite the original file with a pointer file. + * - 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 + * hard linked file (the others are simply pointers). + * - In the same JobId, we accept the last copy of any other + * file -- in particular directories. + * + * All the code to set ok could be condensed to a single + * line, but it would be even harder to read. */ - if (!new_node->hard_link || hard_link) { - new_node->hard_link = hard_link; - new_node->FileIndex = atoi(row[2]); - new_node->JobId = (JobId_t)str_to_int64(row[3]); - new_node->type = type; - new_node->extract = true; /* extract all by default */ - new_node->soft_link = S_ISLNK(statp.st_mode) != 0; - if (type == TN_DIR || type == TN_DIR_NLS) { - new_node->extract_dir = true; /* if dir, extract it */ + ok = true; + if (!node->inserted && JobId == node->JobId) { + if ((hard_link && FileIndex > node->FileIndex) || + (!hard_link && FileIndex < node->FileIndex)) { + ok = false; + } + } + if (ok) { + node->hard_link = hard_link; + node->FileIndex = FileIndex; + node->JobId = JobId; + node->type = type; + node->soft_link = S_ISLNK(statp.st_mode) != 0; + 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 */ + } + } + } + if (node->inserted) { + tree->FileCount++; + if (tree->DeltaCount > 0 && (tree->FileCount-tree->LastCount) > tree->DeltaCount) { + bsendmsg(tree->ua, "+"); + tree->LastCount = tree->FileCount; } } tree->cnt++; @@ -206,7 +217,7 @@ int insert_tree_handler(void *ctx, int num_fields, char **row) /* * Set extract to value passed. We recursively walk - * down the tree setting all children if the + * down the tree setting all children if the * node is a directory. */ static int set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, bool extract) @@ -224,20 +235,20 @@ static int set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, bool extr count++; } /* For a non-file (i.e. directory), we see all the children */ - if (node->type != TN_FILE || (node->soft_link && node->child)) { + if (node->type != TN_FILE || (node->soft_link && tree_node_has_child(node))) { /* Recursive set children within directory */ - for (n=node->child; n; n=n->sibling) { - count += set_extract(ua, n, tree, extract); + foreach_child(n, node) { + count += set_extract(ua, n, tree, extract); } /* * Walk up tree marking any unextracted parent to be * extracted. */ if (extract) { - while (node->parent && !node->parent->extract_dir) { - node = node->parent; - node->extract_dir = true; - } + while (node->parent && !node->parent->extract_dir) { + node = node->parent; + node->extract_dir = true; + } } } else if (extract) { char cwd[2000]; @@ -250,53 +261,57 @@ static int set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, bool extr 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 */ - /* - * 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. - */ - 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; - } - } - } + int32_t LinkFI; + decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */ + /* + * 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. + */ + 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; + } + } + } } } return count; } /* - * Recursively mark the current directory to be restored as + * Recursively mark the current directory to be restored as * well as all directories and files below it. */ static int markcmd(UAContext *ua, TREE_CTX *tree) { TREE_NODE *node; int count = 0; + char ec1[50]; - if (ua->argc < 2 || !tree->node->child) { + if (ua->argc < 2 || !tree_node_has_child(tree->node)) { bsendmsg(ua, _("No files marked.\n")); return 1; } for (int i=1; i < ua->argc; i++) { - for (node = tree->node->child; node; node=node->sibling) { - if (fnmatch(ua->argk[i], node->fname, 0) == 0) { - count += set_extract(ua, node, tree, true); - } + foreach_child(node, tree->node) { + if (fnmatch(ua->argk[i], node->fname, 0) == 0) { + count += set_extract(ua, node, tree, true); + } } } if (count == 0) { bsendmsg(ua, _("No files marked.\n")); + } else if (count == 1) { + bsendmsg(ua, _("1 file marked.\n")); } else { - bsendmsg(ua, _("%d file%s marked.\n"), count, count==0?"":"s"); + bsendmsg(ua, _("%s files marked.\n"), + edit_uint64_with_commas(count, ec1)); } return 1; } @@ -305,25 +320,29 @@ static int markdircmd(UAContext *ua, TREE_CTX *tree) { TREE_NODE *node; int count = 0; + char ec1[50]; - if (ua->argc < 2 || !tree->node->child) { + if (ua->argc < 2 || !tree_node_has_child(tree->node)) { bsendmsg(ua, _("No files marked.\n")); return 1; } for (int i=1; i < ua->argc; i++) { - for (node = tree->node->child; node; node=node->sibling) { - if (fnmatch(ua->argk[i], node->fname, 0) == 0) { - if (node->type == TN_DIR || node->type == TN_DIR_NLS) { - node->extract_dir = true; - count++; - } - } + foreach_child(node, tree->node) { + if (fnmatch(ua->argk[i], node->fname, 0) == 0) { + if (node->type == TN_DIR || node->type == TN_DIR_NLS) { + node->extract_dir = true; + count++; + } + } } } if (count == 0) { bsendmsg(ua, _("No directories marked.\n")); + } else if (count == 1) { + bsendmsg(ua, _("1 directory marked.\n")); } else { - bsendmsg(ua, _("%d director%s marked.\n"), count, count==1?"y":"ies"); + bsendmsg(ua, _("%s directories marked.\n"), + edit_uint64_with_commas(count, ec1)); } return 1; } @@ -332,17 +351,20 @@ static int markdircmd(UAContext *ua, TREE_CTX *tree) static int countcmd(UAContext *ua, TREE_CTX *tree) { int total, num_extract; + char ec1[50], ec2[50]; total = num_extract = 0; for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) { if (node->type != TN_NEWDIR) { - total++; - if (node->extract || node->extract_dir) { - num_extract++; - } + total++; + if (node->extract || node->extract_dir) { + num_extract++; + } } } - bsendmsg(ua, "%d total files/dirs. %d marked to be restored.\n", total, num_extract); + bsendmsg(ua, _("%s total files/dirs. %s marked to be restored.\n"), + edit_uint64_with_commas(total, ec1), + edit_uint64_with_commas(num_extract, ec2)); return 1; } @@ -354,21 +376,21 @@ static int findcmd(UAContext *ua, TREE_CTX *tree) bsendmsg(ua, _("No file specification given.\n")); return 0; } - + for (int i=1; i < ua->argc; i++) { for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) { - if (fnmatch(ua->argk[i], node->fname, 0) == 0) { - char *tag; - tree_getpath(node, cwd, sizeof(cwd)); - if (node->extract) { + if (fnmatch(ua->argk[i], node->fname, 0) == 0) { + const char *tag; + tree_getpath(node, cwd, sizeof(cwd)); + if (node->extract) { tag = "*"; - } else if (node->extract_dir) { + } else if (node->extract_dir) { tag = "+"; - } else { + } else { tag = ""; - } + } bsendmsg(ua, "%s%s\n", tag, cwd); - } + } } } return 1; @@ -380,20 +402,20 @@ static int lscmd(UAContext *ua, TREE_CTX *tree) { TREE_NODE *node; - if (!tree->node->child) { + if (!tree_node_has_child(tree->node)) { return 1; } - for (node = tree->node->child; node; node=node->sibling) { + foreach_child(node, tree->node) { if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) { - char *tag; - if (node->extract) { + const char *tag; + if (node->extract) { tag = "*"; - } else if (node->extract_dir) { + } else if (node->extract_dir) { tag = "+"; - } else { + } else { tag = ""; - } - bsendmsg(ua, "%s%s%s\n", tag, node->fname, node->child?"/":""); + } + bsendmsg(ua, "%s%s%s\n", tag, node->fname, tree_node_has_child(node)?"/":""); } } return 1; @@ -402,64 +424,94 @@ static int lscmd(UAContext *ua, TREE_CTX *tree) /* * Ls command that lists only the marked files */ -static int lsmarkcmd(UAContext *ua, TREE_CTX *tree) +static void rlsmark(UAContext *ua, TREE_NODE *tnode) { TREE_NODE *node; - - if (!tree->node->child) { - return 1; + if (!tree_node_has_child(tnode)) { + return; } - for (node = tree->node->child; node; node=node->sibling) { + foreach_child(node, tnode) { if ((ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) && - (node->extract || node->extract_dir)) { - char *tag; - if (node->extract) { + (node->extract || node->extract_dir)) { + const char *tag; + if (node->extract) { tag = "*"; - } else if (node->extract_dir) { + } else if (node->extract_dir) { tag = "+"; - } else { + } else { tag = ""; - } - bsendmsg(ua, "%s%s%s\n", tag, node->fname, node->child?"/":""); + } + bsendmsg(ua, "%s%s%s\n", tag, node->fname, tree_node_has_child(node)?"/":""); + if (tree_node_has_child(node)) { + rlsmark(ua, node); + } } } +} + +static int lsmarkcmd(UAContext *ua, TREE_CTX *tree) +{ + rlsmark(ua, tree->node); return 1; } -extern char *getuser(uid_t uid); -extern char *getgroup(gid_t gid); + +extern char *getuser(uid_t uid, char *name, int len); +extern char *getgroup(gid_t gid, char *name, int len); /* * This is actually the long form used for "dir" */ -static void ls_output(char *buf, char *fname, char *tag, struct stat *statp) +static void ls_output(char *buf, const char *fname, const char *tag, + struct stat *statp, bool dot_cmd) + { - char *p, *f; + char *p; + const char *f; char ec1[30]; + char en1[30], en2[30]; int n; + time_t time; p = encode_mode(statp->st_mode, buf); - n = sprintf(p, " %2d ", (uint32_t)statp->st_nlink); - p += n; - n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid)); - p += n; - n = sprintf(p, "%8.8s ", edit_uint64(statp->st_size, ec1)); - p += n; - p = encode_time(statp->st_ctime, p); - *p++ = ' '; - *p++ = *tag; + if (dot_cmd) { + *p++ = ','; + n = sprintf(p, "%d,", (uint32_t)statp->st_nlink); + p += n; + n = sprintf(p, "%s,%s,", getuser(statp->st_uid, en1, sizeof(en1)), + getgroup(statp->st_gid, en2, sizeof(en2))); + p += n; + n = sprintf(p, "%s,", edit_uint64(statp->st_size, ec1)); + p += n; + p = encode_time(statp->st_mtime, p); + *p++ = ','; + *p++ = *tag; + *p++ = ','; + } else { + n = sprintf(p, " %2d ", (uint32_t)statp->st_nlink); + p += n; + n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid, en1, sizeof(en1)), + getgroup(statp->st_gid, en2, sizeof(en2))); + p += n; + n = sprintf(p, "%10.10s ", edit_uint64(statp->st_size, ec1)); + p += n; + time = statp->st_mtime; + /* Display most recent time */ + p = encode_time(time, p); + *p++ = ' '; + *p++ = *tag; + } for (f=fname; *f; ) { *p++ = *f++; } *p = 0; } - /* * Like ls command, but give more detail on each file */ -static int dircmd(UAContext *ua, TREE_CTX *tree) +static int do_dircmd(UAContext *ua, TREE_CTX *tree, bool dot_cmd) { TREE_NODE *node; FILE_DBR fdbr; @@ -467,53 +519,65 @@ static int dircmd(UAContext *ua, TREE_CTX *tree) char buf[1100]; char cwd[1100], *pcwd; - if (!tree->node->child) { + if (!tree_node_has_child(tree->node)) { + bsendmsg(ua, _("Node %s has no children.\n"), tree->node->fname); return 1; } - for (node = tree->node->child; node; node=node->sibling) { - char *tag; + + foreach_child(node, tree->node) { + const char *tag; if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) { - if (node->extract) { + if (node->extract) { tag = "*"; - } else if (node->extract_dir) { + } else if (node->extract_dir) { tag = "+"; - } else { + } else { tag = " "; - } - tree_getpath(node, cwd, sizeof(cwd)); - fdbr.FileId = 0; - fdbr.JobId = node->JobId; - /* - * Strip / from soft links to directories. - * This is because soft links to files have a trailing slash - * when returned from tree_getpath, but db_get_file_attr... - * treats soft links as files, so they do not have a trailing - * slash like directory names. - */ - if (node->type == TN_FILE && node->child) { - bstrncpy(buf, cwd, sizeof(buf)); - pcwd = buf; - int len = strlen(buf); - if (len > 1) { - buf[len-1] = 0; /* strip trailing / */ - } - } else { - pcwd = cwd; - } - 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 */ - } else { - /* Something went wrong getting attributes -- print name */ - memset(&statp, 0, sizeof(statp)); - } - ls_output(buf, cwd, tag, &statp); + } + tree_getpath(node, cwd, sizeof(cwd)); + fdbr.FileId = 0; + fdbr.JobId = node->JobId; + /* + * Strip / from soft links to directories. + * This is because soft links to files have a trailing slash + * when returned from tree_getpath, but db_get_file_attr... + * treats soft links as files, so they do not have a trailing + * slash like directory names. + */ + if (node->type == TN_FILE && tree_node_has_child(node)) { + bstrncpy(buf, cwd, sizeof(buf)); + pcwd = buf; + int len = strlen(buf); + if (len > 1) { + buf[len-1] = 0; /* strip trailing / */ + } + } else { + pcwd = cwd; + } + 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 */ + } else { + /* Something went wrong getting attributes -- print name */ + memset(&statp, 0, sizeof(statp)); + } + ls_output(buf, cwd, tag, &statp, dot_cmd); bsendmsg(ua, "%s\n", buf); } } return 1; } +int dot_dircmd(UAContext *ua, TREE_CTX *tree) +{ + return do_dircmd(ua, tree, true/*dot command*/); +} + +static int dircmd(UAContext *ua, TREE_CTX *tree) +{ + return do_dircmd(ua, tree, false/*not dot command*/); +} + static int estimatecmd(UAContext *ua, TREE_CTX *tree) { @@ -527,52 +591,54 @@ static int estimatecmd(UAContext *ua, TREE_CTX *tree) total = num_extract = 0; for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) { if (node->type != TN_NEWDIR) { - total++; - /* If regular file, get size */ - if (node->extract && node->type == TN_FILE) { - num_extract++; - tree_getpath(node, cwd, sizeof(cwd)); - fdbr.FileId = 0; - 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 */ - if (S_ISREG(statp.st_mode) && statp.st_size > 0) { - total_bytes += statp.st_size; - } - } - /* Directory, count only */ - } else if (node->extract || node->extract_dir) { - num_extract++; - } + total++; + /* If regular file, get size */ + if (node->extract && node->type == TN_FILE) { + num_extract++; + tree_getpath(node, cwd, sizeof(cwd)); + fdbr.FileId = 0; + 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 */ + if (S_ISREG(statp.st_mode) && statp.st_size > 0) { + total_bytes += statp.st_size; + } + } + /* Directory, count only */ + } else if (node->extract || node->extract_dir) { + num_extract++; + } } } - bsendmsg(ua, "%d total files; %d marked to be restored; %s bytes.\n", - total, num_extract, edit_uint64_with_commas(total_bytes, ec1)); + bsendmsg(ua, _("%d total files; %d marked to be restored; %s bytes.\n"), + total, num_extract, edit_uint64_with_commas(total_bytes, ec1)); return 1; } -static int helpcmd(UAContext *ua, TREE_CTX *tree) +static int helpcmd(UAContext *ua, TREE_CTX *tree) { unsigned int i; -/* usage(); */ bsendmsg(ua, _(" Command Description\n ======= ===========\n")); for (i=0; iargc != 2) { return 1; } + strip_leading_space(ua->argk[1]); node = tree_cwd(ua->argk[1], tree->root, tree->node); if (!node) { /* Try once more if Win32 drive -- make absolute */ if (ua->argk[1][1] == ':') { /* win32 drive */ bstrncpy(cwd, "/", sizeof(cwd)); - bstrncat(cwd, ua->argk[1], sizeof(cwd)); - node = tree_cwd(cwd, tree->root, tree->node); + bstrncat(cwd, ua->argk[1], sizeof(cwd)); + node = tree_cwd(cwd, tree->root, tree->node); } if (!node) { bsendmsg(ua, _("Invalid path given.\n")); } else { - tree->node = node; + tree->node = node; } } else { tree->node = node; @@ -601,7 +668,7 @@ static int cdcmd(UAContext *ua, TREE_CTX *tree) return 1; } -static int pwdcmd(UAContext *ua, TREE_CTX *tree) +static int pwdcmd(UAContext *ua, TREE_CTX *tree) { char cwd[2000]; tree_getpath(tree->node, cwd, sizeof(cwd)); @@ -615,21 +682,23 @@ static int unmarkcmd(UAContext *ua, TREE_CTX *tree) TREE_NODE *node; int count = 0; - if (ua->argc < 2 || !tree->node->child) { + if (ua->argc < 2 || !tree_node_has_child(tree->node)) { bsendmsg(ua, _("No files unmarked.\n")); return 1; } for (int i=1; i < ua->argc; i++) { - for (node = tree->node->child; node; node=node->sibling) { - if (fnmatch(ua->argk[i], node->fname, 0) == 0) { - count += set_extract(ua, node, tree, false); - } + foreach_child(node, tree->node) { + if (fnmatch(ua->argk[i], node->fname, 0) == 0) { + count += set_extract(ua, node, tree, false); + } } } if (count == 0) { bsendmsg(ua, _("No files unmarked.\n")); + } else if (count == 1) { + bsendmsg(ua, _("1 file unmarked.\n")); } else { - bsendmsg(ua, _("%d file%s unmarked.\n"), count, count==0?"":"s"); + bsendmsg(ua, _("%d files unmarked.\n"), count); } return 1; } @@ -639,37 +708,39 @@ static int unmarkdircmd(UAContext *ua, TREE_CTX *tree) TREE_NODE *node; int count = 0; - if (ua->argc < 2 || !tree->node->child) { + if (ua->argc < 2 || !tree_node_has_child(tree->node)) { bsendmsg(ua, _("No directories unmarked.\n")); return 1; } for (int i=1; i < ua->argc; i++) { - for (node = tree->node->child; node; node=node->sibling) { - if (fnmatch(ua->argk[i], node->fname, 0) == 0) { - if (node->type == TN_DIR || node->type == TN_DIR_NLS) { - node->extract_dir = false; - count++; - } - } + foreach_child(node, tree->node) { + if (fnmatch(ua->argk[i], node->fname, 0) == 0) { + if (node->type == TN_DIR || node->type == TN_DIR_NLS) { + node->extract_dir = false; + count++; + } + } } } if (count == 0) { bsendmsg(ua, _("No directories unmarked.\n")); + } else if (count == 1) { + bsendmsg(ua, _("1 directory unmarked.\n")); } else { - bsendmsg(ua, _("%d director%s unmarked.\n"), count, count==1?"y":"ies"); + bsendmsg(ua, _("%d directories unmarked.\n"), count); } return 1; } -static int donecmd(UAContext *ua, TREE_CTX *tree) +static int donecmd(UAContext *ua, TREE_CTX *tree) { return 0; } -static int quitcmd(UAContext *ua, TREE_CTX *tree) +static int quitcmd(UAContext *ua, TREE_CTX *tree) { ua->quit = true; return 0;