--sd.conf password does not match dir.conf storage password
=======
+- Fix catalog filename truncation in sql_get and elsewhere. Use
+ only a single filename split routine.
- Add command to reset VolFiles to a larger value (don't allow
a smaller number or print big warning).
- Make SD disallow writing on Volume with fewer files than in
db_escape_string(buf, file, fnl);
fdbr->FilenameId = db_get_filename_record(mdb, buf);
- Dmsg1(50, "db_get_filename_record FilenameId=%d\n", fdbr->FilenameId);
+ Dmsg2(100, "db_get_filename_record FilenameId=%d file=%s\n", fdbr->FilenameId, buf);
db_escape_string(buf, spath, pnl);
fdbr->PathId = db_get_path_record(mdb, buf);
- Dmsg2(50, "db_get_path_record PathId=%d path=%s\n", fdbr->PathId, buf);
+ Dmsg2(100, "db_get_path_record PathId=%d path=%s\n", fdbr->PathId, buf);
id = db_get_file_record(mdb, fdbr);
return stat;
}
+/* Get FileSet Record
+ * If the FileSetId is non-zero, we get its record,
+ * otherwise, we search on the name
+ *
+ * Returns: 0 on failure
+ * id on success
+ */
+int db_get_fileset_record(B_DB *mdb, FILESET_DBR *fsr)
+{
+ SQL_ROW row;
+ int stat = 0;
+
+ db_lock(mdb);
+ if (fsr->FileSetId != 0) { /* find by id */
+ Mmsg(&mdb->cmd,
+ "SELECT FileSetId, FileSet, MD5 FROM FileSet "
+ "WHERE FileSetId=%u", fsr->FileSetId);
+ } else { /* find by name */
+ Mmsg(&mdb->cmd,
+ "SELECT FileSetId, FileSet, MD5 FROM FileSet "
+ "WHERE FileSet='%s'", fsr->FileSet);
+ }
+
+ if (QUERY_DB(mdb, mdb->cmd)) {
+ mdb->num_rows = sql_num_rows(mdb);
+ if (mdb->num_rows > 1) {
+ char ed1[30];
+ Mmsg1(&mdb->errmsg, _("More than one Pool!: %s\n"),
+ edit_uint64(mdb->num_rows, ed1));
+ } else if (mdb->num_rows == 1) {
+ if ((row = sql_fetch_row(mdb)) == NULL) {
+ Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
+ } else {
+ fsr->FileSetId = atoi(row[0]);
+ strcpy(fsr->FileSet, row[1]);
+ strcpy(fsr->MD5, row[2]);
+ stat = fsr->FileSetId;
+ }
+ }
+ sql_free_result(mdb);
+ }
+ db_unlock(mdb);
+ return stat;
+}
+
/*
* Get the number of Media records
/* ======= ua_restore.c */
+/* List last 20 Jobs */
char *uar_list_jobs =
"SELECT JobId,Client.Name as Client,StartTime,Type as "
"JobType,JobFiles,JobBytes "
"FROM Client,Job WHERE Client.ClientId=Job.ClientId AND JobStatus='T' "
"LIMIT 20";
+#ifdef HAVE_MYSQL
+/* MYSQL IS NOT STANDARD SQL !!!!! */
+/* List Jobs where a particular file is saved */
char *uar_file =
"SELECT Job.JobId as JobId, Client.Name as Client, "
"CONCAT(Path.Path,Filename.Name) as Name, "
"AND JobStatus='T' AND Job.JobId=File.JobId "
"AND Path.PathId=File.PathId AND Filename.FilenameId=File.FilenameId "
"AND Filename.Name='%s' LIMIT 20";
+#else
+/* List Jobs where a particular file is saved */
+char *uar_file =
+ "SELECT Job.JobId as JobId, Client.Name as Client, "
+ "Path.Path||Filename.Name as Name, "
+ "StartTime,Type as JobType,JobFiles,JobBytes "
+ "FROM Client,Job,File,Filename,Path WHERE Client.ClientId=Job.ClientId "
+ "AND JobStatus='T' AND Job.JobId=File.JobId "
+ "AND Path.PathId=File.PathId AND Filename.FilenameId=File.FilenameId "
+ "AND Filename.Name='%s' LIMIT 20";
+#endif
+
char *uar_sel_files =
"SELECT Path.Path,Filename.Name,FileIndex,JobId "
"AND Level='F' AND JobStatus='T' "
"AND JobMedia.JobId=Job.JobId "
"AND JobMedia.MediaId=Media.MediaId "
+ "AND Job.FileSetId=%u "
"ORDER BY Job.JobTDate DESC LIMIT 1";
char *uar_full =
"AND JobMedia.JobId=Job.JobId "
"AND JobMedia.MediaId=Media.MediaId "
"AND Job.Level='I' AND JobStatus='T' "
+ "AND Job.FileSetId=%u "
"GROUP BY Job.JobId";
char *uar_list_temp =
char *uar_sel_jobid_temp = "SELECT JobId FROM temp";
char *uar_sel_all_temp1 = "SELECT * FROM temp1";
+
+char *uar_sel_fileset =
+ "SELECT FileSet.FileSetId,FileSet.FileSet FROM Job,"
+ "Client,FileSet WHERE Job.FileSetId=FileSet.FileSetId "
+ "AND Job.ClientId=Client.ClientId AND Client.Name='%s' "
+ "GROUP BY FileSetId";
extern int runcmd(UAContext *ua, char *cmd);
/* Imported variables */
-extern char *uar_list_jobs;
-extern char *uar_file;
-extern char *uar_sel_files;
-extern char *uar_del_temp;
-extern char *uar_del_temp1;
-extern char *uar_create_temp;
-extern char *uar_create_temp1;
-extern char *uar_last_full;
-extern char *uar_full;
-extern char *uar_inc;
-extern char *uar_list_temp;
-extern char *uar_sel_jobid_temp;
-extern char *uar_sel_all_temp1;
+extern char *uar_list_jobs, *uar_file, *uar_sel_files;
+extern char *uar_del_temp, *uar_del_temp1, *uar_create_temp;
+extern char *uar_create_temp1, *uar_last_full, *uar_full;
+extern char *uar_inc, *uar_list_temp, *uar_sel_jobid_temp;
+extern char *uar_sel_all_temp1, *uar_sel_fileset;
/* Context for insert_tree_handler() */
typedef struct s_tree_ctx {
static int next_jobid_from_list(char **p, uint32_t *JobId);
static int user_select_jobids(UAContext *ua, JobIds *ji);
static void user_select_files(TREE_CTX *tree);
+static int fileset_handler(void *ctx, int num_fields, char **row);
/*
job->hdr.name, working_directory);
}
- Dmsg1(000, "Submitting: %s\n", ua->cmd);
+ Dmsg1(400, "Submitting: %s\n", ua->cmd);
parse_command_args(ua);
runcmd(ua, ua->cmd);
*/
static int user_select_jobids(UAContext *ua, JobIds *ji)
{
+ char fileset_name[MAX_NAME_LENGTH];
char *p;
+ FILESET_DBR fsr;
JobId_t JobId;
JOB_DBR jr;
POOLMEM *query;
done = 0;
break;
case 4: /* Select the most recent backups */
+ query = get_pool_memory(PM_MESSAGE);
db_sql_query(ua->db, uar_del_temp, NULL, NULL);
db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
bsendmsg(ua, "%s\n", db_strerror(ua->db));
}
+ /*
+ * Select Client
+ */
if (!(ji->client = get_client_resource(ua))) {
return 0;
}
- query = get_pool_memory(PM_MESSAGE);
- Mmsg(&query, uar_last_full, ji->client->hdr.name);
+
+ /*
+ * Select FileSet
+ */
+ Mmsg(&query, uar_sel_fileset, ji->client->hdr.name);
+ start_prompt(ua, _("The defined FileSet resources are:\n"));
+ if (!db_sql_query(ua->db, query, fileset_handler, (void *)ua)) {
+ bsendmsg(ua, "%s\n", db_strerror(ua->db));
+ }
+ if (do_prompt(ua, _("Select FileSet resource"), fileset_name) < 0) {
+ free_pool_memory(query);
+ return 0;
+ }
+ fsr.FileSetId = 0;
+ strcpy(fsr.FileSet, fileset_name);
+ if (!db_get_fileset_record(ua->db, &fsr)) {
+ bsendmsg(ua, "%s\n", db_strerror(ua->db));
+ free_pool_memory(query);
+ return 0;
+ }
+
+ Mmsg(&query, uar_last_full, ji->client->hdr.name, fsr.FileSetId);
/* Find JobId of full Backup of system */
if (!db_sql_query(ua->db, query, NULL, NULL)) {
bsendmsg(ua, "%s\n", db_strerror(ua->db));
bsendmsg(ua, "%s\n", db_strerror(ua->db));
}
/* Now find all Incremental Jobs */
- Mmsg(&query, uar_inc, (uint32_t)ji->JobTDate, ji->ClientId);
+ Mmsg(&query, uar_inc, (uint32_t)ji->JobTDate, ji->ClientId, fsr.FileSetId);
if (!db_sql_query(ua->db, query, NULL, NULL)) {
bsendmsg(ua, "%s\n", db_strerror(ua->db));
}
return 0;
}
-
-
+/*
+ * Callback handler build fileset prompt list
+ */
+static int fileset_handler(void *ctx, int num_fields, char **row)
+{
+ add_prompt((UAContext *)ctx, row[1]);
+ return 0;
+}
/* Forward referenced commands */
static int countcmd(UAContext *ua, TREE_CTX *tree);
static int findcmd(UAContext *ua, TREE_CTX *tree);
static int lscmd(UAContext *ua, TREE_CTX *tree);
+static int dircmd(UAContext *ua, TREE_CTX *tree);
static int helpcmd(UAContext *ua, TREE_CTX *tree);
static int cdcmd(UAContext *ua, TREE_CTX *tree);
static int pwdcmd(UAContext *ua, TREE_CTX *tree);
{ N_("cd"), cdcmd, _("change current directory")},
{ N_("pwd"), pwdcmd, _("print current working directory")},
{ N_("ls"), lscmd, _("list current directory")},
- { N_("dir"), lscmd, _("list current directory")},
+ { N_("dir"), dircmd, _("list current directory")},
{ N_("count"), countcmd, _("count marked files")},
{ N_("find"), findcmd, _("find files")},
{ N_("done"), quitcmd, _("leave file selection mode")},
return 1;
}
+extern char *getuser(uid_t uid);
+extern char *getgroup(gid_t gid);
+
+static void ls_output(char *buf, char *fname, struct stat *statp)
+{
+ char *p, *f;
+ int n;
+
+ 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, "%8ld ", statp->st_size);
+ p += n;
+ p = encode_time(statp->st_ctime, p);
+ *p++ = ' ';
+ *p++ = ' ';
+ 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)
+{
+ TREE_NODE *node;
+ FILE_DBR fdbr;
+ struct stat statp;
+ char buf[1000];
+ char cwd[1100];
+
+ if (!tree->node->child) {
+ return 1;
+ }
+ for (node = tree->node->child; node; node=node->sibling) {
+ if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) {
+ tree_getpath(node, cwd, sizeof(cwd));
+ fdbr.FileId = 0;
+ fdbr.JobId = node->JobId;
+ if (db_get_file_attributes_record(ua->db, cwd, &fdbr)) {
+ decode_stat(fdbr.LStat, &statp); /* decode stat pkt */
+ ls_output(buf, cwd, &statp);
+ bsendmsg(ua, "%s\n", buf);
+ }
+ }
+ }
+ return 1;
+}
+
+
static int helpcmd(UAContext *ua, TREE_CTX *tree)
{
unsigned int i;
LIBSRCS = find.c match.c find_one.c
LIBOBJS = find.o match.o find_one.o
-FINDSRCS = testfind.c
-FINDOBJS = testfind.o
-
.SUFFIXES: .c .o
.PHONY:
.DONTCARE:
$(AR) cru $@ $(LIBOBJS)
$(RANLIB) $@
-testfind: libfind.a $(FINDOBJS)
- $(CC) -g $(LDFLAGS) -L. -L../lib -o $@ $(FINDOBJS) $(LIBS) $(DLIB) -lfind -lbac -lm
-
Makefile: $(srcdir)/Makefile.in $(topdir)/config.status
cd $(topdir) \
&& CONFIG_FILES=$(thisdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
clean:
- $(RMF) find testfind core a.out *.a *.o *.bak *~ *.intpro *.extpro 1 2 3
+ $(RMF) find core a.out *.a *.o *.bak *~ *.intpro *.extpro 1 2 3
realclean: clean
$(RMF) tags
--- /dev/null
+/*
+ * Test program for find files
+ */
+
+#include "bacula.h"
+#include "findlib/find.h"
+#include "jcr.h"
+
+
+/* Global variables */
+static int num_files = 0;
+static int max_file_len = 0;
+static int max_path_len = 0;
+static int trunc_fname = 0;
+static int trunc_path = 0;
+
+
+static int print_file(FF_PKT *ff, void *pkt);
+static void count_files(FF_PKT *ff);
+
+static void usage()
+{
+ fprintf(stderr, _(
+"\n"
+"Usage: testfind [-d debug_level] [-] [pattern1 ...]\n"
+" -dnn set debug level to nn\n"
+" - read pattern(s) from stdin\n"
+" -? print this message.\n"
+"\n"
+"Patterns are file inclusion -- normally directories.\n"
+"Debug level >= 1 prints each file found.\n"
+"Debug level >= 10 prints path/file for catalog.\n"
+"Errors always printed.\n"
+"Files/paths truncated is number with len > 255.\n"
+"Truncation is only in catalog.\n"
+"\n"));
+
+ exit(1);
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+ FF_PKT *ff;
+ char name[1000];
+ int i, ch;
+
+ while ((ch = getopt(argc, argv, "d:?")) != -1) {
+ switch (ch) {
+ case 'd': /* set debug level */
+ debug_level = atoi(optarg);
+ if (debug_level <= 0) {
+ debug_level = 1;
+ }
+ break;
+
+ case '?':
+ default:
+ usage();
+
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ ff = init_find_files();
+ if (argc == 0) {
+ add_fname_to_include_list(ff, 0, "/"); /* default to / */
+ } else {
+ for (i=0; i < argc; i++) {
+ if (strcmp(argv[i], "-") == 0) {
+ while (fgets(name, sizeof(name)-1, stdin)) {
+ strip_trailing_junk(name);
+ add_fname_to_include_list(ff, 0, name);
+ }
+ continue;
+ }
+ add_fname_to_include_list(ff, 0, argv[i]);
+ }
+ }
+
+ find_files(ff, print_file, NULL);
+ term_find_files(ff);
+
+ printf(_("\
+Total files : %d\n\
+Max file length: %d\n\
+Max path length: %d\n\
+Files truncated: %d\n\
+Paths truncated: %d\n"),
+ num_files, max_file_len, max_path_len,
+ trunc_fname, trunc_path);
+
+ sm_dump(False);
+ exit(0);
+}
+
+static int print_file(FF_PKT *ff, void *pkt)
+{
+ switch (ff->type) {
+ case FT_LNKSAVED:
+ if (debug_level == 1) {
+ printf("%s\n", ff->fname);
+ } else if (debug_level > 1) {
+ printf("Lnka: %s -> %s\n", ff->fname, ff->link);
+ }
+ break;
+ case FT_REGE:
+ if (debug_level == 1) {
+ printf("%s\n", ff->fname);
+ } else if (debug_level > 1) {
+ printf("Empty: %s\n", ff->fname);
+ }
+ count_files(ff);
+ break;
+ case FT_REG:
+ if (debug_level == 1) {
+ printf("%s\n", ff->fname);
+ } else if (debug_level > 1) {
+ printf("Reg: %s\n", ff->fname);
+ }
+ count_files(ff);
+ break;
+ case FT_LNK:
+ if (debug_level == 1) {
+ printf("%s\n", ff->fname);
+ } else if (debug_level > 1) {
+ printf("Lnk: %s -> %s\n", ff->fname, ff->link);
+ }
+ count_files(ff);
+ break;
+ case FT_DIR:
+ if (debug_level == 1) {
+ printf("%s\n", ff->fname);
+ } else if (debug_level > 1) {
+ printf("Dir: %s\n", ff->fname);
+ }
+ count_files(ff);
+ break;
+ case FT_SPEC:
+ if (debug_level == 1) {
+ printf("%s\n", ff->fname);
+ } else if (debug_level > 1) {
+ printf("Spec: %s\n", ff->fname);
+ }
+ count_files(ff);
+ break;
+ case FT_NOACCESS:
+ printf(_("Err: Could not access %s: %s\n"), ff->fname, strerror(errno));
+ break;
+ case FT_NOFOLLOW:
+ printf(_("Err: Could not follow ff->link %s: %s\n"), ff->fname, strerror(errno));
+ break;
+ case FT_NOSTAT:
+ printf(_("Err: Could not stat %s: %s\n"), ff->fname, strerror(errno));
+ break;
+ case FT_NOCHG:
+ printf(_("Skip: File not saved. No change. %s\n"), ff->fname);
+ break;
+ case FT_ISARCH:
+ printf(_("Err: Attempt to backup archive. Not saved. %s\n"), ff->fname);
+ break;
+ case FT_NORECURSE:
+ printf(_("Recursion turned off. Directory not entered. %s\n"), ff->fname);
+ break;
+ case FT_NOFSCHG:
+ printf(_("Skip: File system change prohibited. Directory not entered. %s\n"), ff->fname);
+ break;
+ case FT_NOOPEN:
+ printf(_("Err: Could not open directory %s: %s\n"), ff->fname, strerror(errno));
+ break;
+ default:
+ printf(_("Err: Unknown file ff->type %d: %s\n"), ff->type, ff->fname);
+ break;
+ }
+ return 1;
+}
+
+static void count_files(FF_PKT *ar)
+{
+ int fnl, pnl;
+ char *l, *p;
+ char file[MAXSTRING];
+ char spath[MAXSTRING];
+
+ num_files++;
+
+ /* Find path without the filename.
+ * I.e. everything after the last / is a "filename".
+ * OK, maybe it is a directory name, but we treat it like
+ * a filename. If we don't find a / then the whole name
+ * must be a path name (e.g. c:).
+ */
+ for (p=l=ar->fname; *p; p++) {
+ if (*p == '/') {
+ l = p; /* set pos of last slash */
+ }
+ }
+ if (*l == '/') { /* did we find a slash? */
+ l++; /* yes, point to filename */
+ } else { /* no, whole thing must be path name */
+ l = p;
+ }
+
+ /* If filename doesn't exist (i.e. root directory), we
+ * simply create a blank name consisting of a single
+ * space. This makes handling zero length filenames
+ * easier.
+ */
+ fnl = p - l;
+ if (fnl > max_file_len) {
+ max_file_len = fnl;
+ }
+ if (fnl > 255) {
+ printf(_("===== Filename truncated to 255 chars: %s\n"), l);
+ fnl = 255;
+ trunc_fname++;
+ }
+ if (fnl > 0) {
+ strncpy(file, l, fnl); /* copy filename */
+ file[fnl] = 0;
+ } else {
+ file[0] = ' '; /* blank filename */
+ file[1] = 0;
+ }
+
+ pnl = l - ar->fname;
+ if (pnl > max_path_len) {
+ max_path_len = pnl;
+ }
+ if (pnl > 255) {
+ printf(_("========== Path name truncated to 255 chars: %s\n"), ar->fname);
+ pnl = 255;
+ trunc_path++;
+ }
+ strncpy(spath, ar->fname, pnl);
+ spath[pnl] = 0;
+ if (pnl == 0) {
+ spath[0] = ' ';
+ spath[1] = 0;
+ printf(_("========== Path length is zero. File=%s\n"), ar->fname);
+ }
+ if (debug_level >= 10) {
+ printf("Path: %s\n", spath);
+ printf("File: %s\n", file);
+ }
+
+}