From: Kern Sibbald Date: Wed, 3 Sep 2003 08:02:45 +0000 (+0000) Subject: cleanup query.sql + restructure ua_restore to handle selection of files X-Git-Tag: Release-7.0.0~10012 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=72b2d0c69658b0268eea14134972ef247a671f76;p=bacula%2Fbacula cleanup query.sql + restructure ua_restore to handle selection of files git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@684 91ce42f0-d328-0410-95d8-f526ca767f89 --- diff --git a/bacula/ChangeLog b/bacula/ChangeLog index fe57758ad6..dcfad2ea2c 100644 --- a/bacula/ChangeLog +++ b/bacula/ChangeLog @@ -1,10 +1,76 @@ +2003-08-02 Version 1.32 02Sep03 Beta +- Eliminate a duplicated query from query.sql +- Restructure ua_restore.c so that I can add restore files. +- Correct positioning problem at beginning of a second volume + introduced with the new forward spacing code. +- Eliminated a sprintf() in the tree routines. +- Doc updates +- Added HOST_OS, DISTNAME, and DISTVER to the status output. +- Eliminated a few stray Dmsgn(000, messages making them either + debug_level 100, or Pmsg(). +- Made the default "Maximum File Size" 1Gbyte in SD. +- Cache path in tree.c to reduce calls to make_tree_path +- Documentation updates +- Implement forward space file and block when reading a bsr. +- Fixed a bug in db_find_next_volume() where the VolStatus was + not being returned. +- Rework some of the query.sql records that were incomplete. +- Fixed an ambigous SQL statement in restore. +- Fix proper sorting order in restore display last 20 jobs. +- Remove duplicate JobId's in feeding the directory tree. +- Fix an orphaned ua buffer due to a return that should have been + break so that cleanup code executed. +- Changed some strcats and sprintfs int bstrncat() ... to prevent + possible buffer overflows. +- Fix max file size code on tape so that after writing EOF, + an appropriate jobmedia record is created. +- Fix error messages in dev.c, which were copied into lots of + different subroutines without updating the text. +- Add reposition_dev(), and make more of the return statuses of + dev routine standard. +- Eliminate old semaphore and workq code. Keep only new jobq code. +- Try to get a better default size for the gnome-console +- Add code to avoid race conditions in starting/stoping the + heartbeat thread. +- Correct potential race condition in heartbeat_stop(). +- Correct segmentation fault in mysql.c if no password given. +- make pm_strcat and pm_strcpy return the string length +- Use Phil's code to get the unadorned job name. +- Move the MTIOCERRSTAT from just after a write() failure to + after writing the EOF marks on FreeBSD systems. +- Enhance packet too big error in bnet.c and add some + ASSERTs in the send code. +- Set heartbeat interval to zero by default. +- Add Recycle to list of Update Volume parameters. +- Use bget_dirmsg() wherever possible in Director. +- Split next_volume code from catreq.c to next_vol.c +- Consolidate editing job codes into a single routine. +- Add Job resource name +- Remove check for Win32 attributes in bls. It doesn't + need to read them, only print ls -l. +- Add SDConnectTimeout in FD. +- Add Scott's perlgui directory +- Upgrade from RH7.3 to RH9. New autoconf. +- Eliminate gnome2-console directory. +- Correct English in status command. +- Eliminate old shell expansion code and use BPIPE to call shell + with echo command. +- Documentation as usual. +- Add a new bacula.spec for Mandrake +- Add ownership to alist items. Default the list owns the items. +- Make record_cb return a status (preparation for internal use). +- Remove all clearing of remainder in read_record.c -- not necessary +- Write a tapetest program for FreeBSD end of tape testing. +- Modify read.c in Bacula to use read_record.c +- Implement multiple records in read_record.c. One for each session that + is open. Free the record with the EOS_LABEL is found (or at the + end of the scan). 2003-08-02 Version 1.31a 02Aug03 Released - Yifang Dai reported a case where he stress tested Bacula and backed up to four volumes, but only two were selected for the restore. This is because I forgot that the selection could span a volume entirely. - - Added a missing CLIENT_FOUND_ROWS to the second attempt to open the MySQL database -- this prevents UPDATE errors if nothing actually changed. diff --git a/bacula/kernstodo b/bacula/kernstodo index dc49d83619..d1644213ac 100644 --- a/bacula/kernstodo +++ b/bacula/kernstodo @@ -1,5 +1,5 @@ Kern's ToDo List - 24 August 2003 + 3 September 2003 Documentation to do: (any release a little bit at a time) - Document running a test version. @@ -15,6 +15,7 @@ Documentation to do: (any release a little bit at a time) - Lookup HP tape replacement recommendations (see trouble shooting autochanger) - Document FInclude ... - Document all the status codes JobLevel, JobType, JobStatus. +- Document SDConnectTimeout (in FD). Testing to do: (painful) - that ALL console command line options work and are always implemented @@ -27,15 +28,26 @@ Testing to do: (painful) - Figure out how to use ssh or stunnel to protect Bacula communications. For 1.32: +- Enhance autochanger interface to include a "scan" feature + scan 1; scan 1-5; scan 1,2,4 ... to update the catalog +- Allow a slot or range of slots on the label barcodes command. + when the magazine is changed. +- Specify list of files to restore +- Implement ClientRunBeforeJob and ClientRunAfterJob. +- Figure out what is interrupting sql command in console. +- Don't print "Warning: Wrong Volume mounted ..." if mounting second + volume. +- Implement List Volume Job=xxx or List scheduled volumes or Status Director +- Make | and < work on FD side. + +For 1.33 +- Why can't SQL do the filename sort for restore? - Is a pool specification really needed for a restore? Yes, and you may want to exclude archive Pools. - Look at libkse (man kse) for FreeBSD threading. - Look into Microsoft Volume Shadowcopy Service VSS for backing up system state components (Active Directory, System Volume, ...) -- Add ExhautiveRestoreSearch and use repositioning at the beginning - of the tape. -- Specify list of files to restore -- Implement ClientRunBeforeJob and ClientRunAfterJob. +- Add ExhautiveRestoreSearch - Look at the possibility of loading only the necessary data into the restore tree (i.e. do it one directory at a time as the user walks through the tree). @@ -49,7 +61,6 @@ For 1.32: - Fix restore to list errors if Invalid block found, and if # files restored does not match # expected. - Prohibit backing up archive device (findlib/find_one.c:128) -- Make | and < work on FD side. - Implement Release Device in the Job resource to unmount a drive. - Implement Acquire Device in the Job resource to mount a drive, be sure this works with admin jobs so that the user can get @@ -57,16 +68,11 @@ For 1.32: run the job but don't save the files. - Add JobName= to VerifyToCatalog so that all verifies can be done at the end. - Implement FileOptions (see end of this document) -- Enhance autochanger interface to include a "scan" feature - scan 1; scan 1-5; scan 1,2,4 ... to update the catalog - when the magazine is changed. - Make things like list where a file is saved case independent for Windows. - Edit the Client/Storage name into authentication failure messages. - Implement job in VerifyToCatalog - Implement migrate -- Implement List Volume Job=xxx or List scheduled volumes or Status Director -- Allow a slot or range of slots on the label barcodes command. - Implement a PostgreSQL driver. - Bacula needs to propagate SD errors. > > cluster-dir: Start Backup JobId 252, Job=REUTERS.2003-08-11_15.04.12 @@ -92,7 +98,6 @@ For 1.32: - Something is not right in last block of fill command. - Implement a Recycle command - Add FileSet to command line arguments for restore. -- Do full check the command line args in update (e.g. VolStatus ...). - Allow multiple Storage specifications (or multiple names on a single Storage specification) in the Job record. Thus a job can be backed up to a number of storage devices. @@ -866,4 +871,5 @@ Done: (see kernsdone for more) - Implement forward spacing between files. - Add Machine type (Linux/Windows) to Status report for daemons. Look at src/host.h - +- Use repositioning at the beginning of the tape. +- Do full check the command line args in update (e.g. VolStatus ...). diff --git a/bacula/src/dird/query.sql b/bacula/src/dird/query.sql index 3ef1a80cf1..70c57f69be 100644 --- a/bacula/src/dird/query.sql +++ b/bacula/src/dird/query.sql @@ -3,23 +3,16 @@ SELECT count(*) AS Jobs, sum(JobFiles) AS Files, sum(JobBytes) AS Bytes, Name AS Job FROM Job GROUP BY Name; SELECT max(JobId) AS Jobs,sum(JobFiles) AS Files, sum(JobBytes) As Bytes FROM Job; -# 2 -:List where a file is saved: -*Enter path with trailing slash: -*Enter filename: -*Enter Client name: -SELECT Job.JobId,StartTime AS JobStartTime,VolumeName,Client.Name AS ClientName - FROM Job,File,Path,Filename,Media,JobMedia,Client - WHERE File.JobId=Job.JobId - AND Path.Path='%1' - AND Filename.Name='%2' - AND Client.Name='%3' - AND Path.PathId=File.PathId - AND Filename.FilenameId=File.FilenameId - AND JobMedia.JobId=Job.JobId - AND JobMedia.MediaId=Media.MediaId - AND Client.ClientId=Job.ClientId - GROUP BY Job.JobId; +# 2 +:List where a File is saved regardless of the directory: +*Enter Filename (no path): +SELECT Job.JobId as JobId, Client.Name as Client, + Path.Path,Filename.Name, + StartTime,Level,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='%1' ORDER BY Job.StartTime LIMIT 20; # 3 :List where the most recent copies of a file are saved: *Enter path with trailing slash: @@ -143,21 +136,11 @@ SELECT Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,MaxVolBytes FROM Pool WHERE Name='%1'; # 11 -:List where a File is saved: -*Enter Filename (no path): -SELECT Job.JobId as JobId, Client.Name as Client, - Path.Path,Filename.Name, - StartTime,Level,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='%1' ORDER BY Job.StartTime LIMIT 20; -# :List total files/bytes by Job: SELECT count(*) AS Jobs, sum(JobFiles) AS Files, sum(JobBytes) AS Bytes, Name AS Job FROM Job GROUP by Name -# +# 12 :List total files/bytes by Volume: SELECT count(*) AS Jobs, sum(JobFiles) AS Files, sum(JobBytes) AS Bytes, VolumeName @@ -165,7 +148,7 @@ SELECT count(*) AS Jobs, sum(JobFiles) AS Files, WHERE JobMedia.JobId=Job.JobId AND JobMedia.MediaId=Media.MediaId GROUP by VolumeName; -# +# 13 :List Files for a selected JobId: *Enter JobId: SELECT Path.Path,Filename.Name FROM File, diff --git a/bacula/src/dird/ua_restore.c b/bacula/src/dird/ua_restore.c index a7565991fe..96697b3524 100644 --- a/bacula/src/dird/ua_restore.c +++ b/bacula/src/dird/ua_restore.c @@ -48,14 +48,19 @@ extern char *uar_inc_dec, *uar_list_temp, *uar_sel_jobid_temp; extern char *uar_sel_all_temp1, *uar_sel_fileset, *uar_mediatype; -/* Main structure for obtaining JobIds */ -struct JOBIDS { +/* Main structure for obtaining JobIds or Files to be restored */ +struct RESTORE_CTX { utime_t JobTDate; uint32_t TotalFiles; char ClientName[MAX_NAME_LENGTH]; char last_jobid[10]; char JobIds[200]; /* User entered string of JobIds */ STORE *store; + JOB *restore_job; + int restore_jobs; + uint32_t selected_files; + char *where; + RBSR *bsr; }; struct NAME_LIST { @@ -73,13 +78,14 @@ struct NAME_LIST { static int last_full_handler(void *ctx, int num_fields, char **row); static int jobid_handler(void *ctx, int num_fields, char **row); static int next_jobid_from_list(char **p, uint32_t *JobId); -static int user_select_jobids(UAContext *ua, JOBIDS *ji); +static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx); static int fileset_handler(void *ctx, int num_fields, char **row); static void print_name_list(UAContext *ua, NAME_LIST *name_list); static int unique_name_list_handler(void *ctx, int num_fields, char **row); static void free_name_list(NAME_LIST *name_list); -static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, JOBIDS *ji); -static int select_backups_before_date(UAContext *ua, JOBIDS *ji, char *date); +static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx); +static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date); +static void build_directory_tree(UAContext *ua, RESTORE_CTX *rx); /* * Restore files @@ -87,141 +93,73 @@ static int select_backups_before_date(UAContext *ua, JOBIDS *ji, char *date); */ int restorecmd(UAContext *ua, char *cmd) { - POOLMEM *query; - TREE_CTX tree; - JobId_t JobId, last_JobId; - char *p; - RBSR *bsr; - char *nofname = ""; - JOBIDS ji; + RESTORE_CTX rx; /* restore context */ JOB *job = NULL; - JOB *restore_job = NULL; - int restore_jobs = 0; - NAME_LIST name_list; - uint32_t selected_files = 0; - char *where = NULL; int i; + memset(&rx, 0, sizeof(rx)); + i = find_arg_with_value(ua, "where"); if (i >= 0) { - where = ua->argv[i]; + rx.where = ua->argv[i]; } if (!open_db(ua)) { return 0; } - memset(&tree, 0, sizeof(TREE_CTX)); - memset(&name_list, 0, sizeof(name_list)); - memset(&ji, 0, sizeof(ji)); - /* Ensure there is at least one Restore Job */ LockRes(); while ( (job = (JOB *)GetNextRes(R_JOB, (RES *)job)) ) { if (job->JobType == JT_RESTORE) { - if (!restore_job) { - restore_job = job; + if (!rx.restore_job) { + rx.restore_job = job; } - restore_jobs++; + rx.restore_jobs++; } } UnlockRes(); - if (!restore_jobs) { + if (!rx.restore_jobs) { bsendmsg(ua, _( "No Restore Job Resource found. You must create at least\n" "one before running this command.\n")); return 0; } + rx.bsr = new_bsr(); /* - * Request user to select JobIds by various different methods + * Request user to select JobIds or files by various different methods * last 20 jobs, where File saved, most recent backup, ... + * In the end, a list of files are pumped into + * add_findex() */ - if (!user_select_jobids(ua, &ji)) { - return 0; - } - - /* - * Build the directory tree containing JobIds user selected - */ - tree.root = new_tree(ji.TotalFiles); - tree.root->fname = nofname; - tree.ua = ua; - query = get_pool_memory(PM_MESSAGE); - last_JobId = 0; - /* - * For display purposes, the same JobId, with different volumes may - * appear more than once, however, we only insert it once. - */ - int items = 0; - for (p=ji.JobIds; next_jobid_from_list(&p, &JobId) > 0; ) { - - if (JobId == last_JobId) { - continue; /* eliminate duplicate JobIds */ - } - last_JobId = JobId; - bsendmsg(ua, _("Building directory tree for JobId %u ...\n"), JobId); - items++; - /* - * Find files for this JobId and insert them in the tree - */ - Mmsg(&query, uar_sel_files, JobId); - if (!db_sql_query(ua->db, query, insert_tree_handler, (void *)&tree)) { - bsendmsg(ua, "%s", db_strerror(ua->db)); - } - /* - * Find the FileSets for this JobId and add to the name_list - */ - Mmsg(&query, uar_mediatype, JobId); - if (!db_sql_query(ua->db, query, unique_name_list_handler, (void *)&name_list)) { - bsendmsg(ua, "%s", db_strerror(ua->db)); - } - } - bsendmsg(ua, "%d Job%s inserted into the tree and marked for extraction.\n", - items, items==1?"":"s"); - free_pool_memory(query); - - /* Check MediaType and select storage that corresponds */ - get_storage_from_mediatype(ua, &name_list, &ji); - free_name_list(&name_list); - - if (find_arg(ua, _("all")) < 0) { - /* Let the user select which files to restore */ - user_select_files_from_tree(&tree); - } - - /* - * Walk down through the tree finding all files marked to be - * extracted making a bootstrap file. - */ - bsr = new_bsr(); - for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) { - Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node); - if (node->extract) { - Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex); - add_findex(bsr, node->JobId, node->FileIndex); - selected_files++; - } + switch (user_select_jobids_or_files(ua, &rx)) { + case 0: + free_bsr(rx.bsr); + return 0; /* error */ + case 1: /* select by jobid */ + build_directory_tree(ua, &rx); + break; + case 2: + break; } - free_tree(tree.root); /* free the directory tree */ - - if (bsr->JobId) { - if (!complete_bsr(ua, bsr)) { /* find Vol, SessId, SessTime from JobIds */ + if (rx.bsr->JobId) { + if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */ bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n")); - free_bsr(bsr); + free_bsr(rx.bsr); return 0; } -// print_bsr(ua, bsr); - write_bsr_file(ua, bsr); - bsendmsg(ua, _("\n%u files selected to restore.\n\n"), selected_files); +// print_bsr(ua, rx.bsr); + write_bsr_file(ua, rx.bsr); + bsendmsg(ua, _("\n%u files selected to restore.\n\n"), rx.selected_files); } else { bsendmsg(ua, _("No files selected to restore.\n")); } - free_bsr(bsr); + free_bsr(rx.bsr); - if (restore_jobs == 1) { - job = restore_job; + if (rx.restore_jobs == 1) { + job = rx.restore_job; } else { job = select_restore_job_resource(ua); } @@ -231,26 +169,26 @@ int restorecmd(UAContext *ua, char *cmd) } /* If no client name specified yet, get it now */ - if (!ji.ClientName[0]) { + if (!rx.ClientName[0]) { CLIENT_DBR cr; memset(&cr, 0, sizeof(cr)); if (!get_client_dbr(ua, &cr)) { return 0; } - bstrncpy(ji.ClientName, cr.Name, sizeof(ji.ClientName)); + bstrncpy(rx.ClientName, cr.Name, sizeof(rx.ClientName)); } /* Build run command */ - if (where) { + if (rx.where) { Mmsg(&ua->cmd, "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\"" " where=\"%s\"", - job->hdr.name, ji.ClientName, ji.store?ji.store->hdr.name:"", - working_directory, where); + job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"", + working_directory, rx.where); } else { Mmsg(&ua->cmd, "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\"", - job->hdr.name, ji.ClientName, ji.store?ji.store->hdr.name:"", + job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"", working_directory); } @@ -267,7 +205,7 @@ int restorecmd(UAContext *ua, char *cmd) * select a list of JobIds from which he will subsequently * select which files are to be restored. */ -static int user_select_jobids(UAContext *ua, JOBIDS *ji) +static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) { char *p; char date[MAX_TIME_LENGTH]; @@ -283,6 +221,7 @@ static int user_select_jobids(UAContext *ua, JOBIDS *ji) "Enter SQL list command", "Select the most recent backup for a client", "Select backup for a client before a specified time", + "Enter a list of files to restore", "Cancel", NULL }; @@ -299,12 +238,12 @@ static int user_select_jobids(UAContext *ua, JOBIDS *ji) if (i < 0) { return 0; } - bstrncpy(ji->JobIds, ua->argv[i], sizeof(ji->JobIds)); + bstrncpy(rx->JobIds, ua->argv[i], sizeof(rx->JobIds)); done = true; break; case 1: /* current */ bstrutime(date, sizeof(date), time(NULL)); - if (!select_backups_before_date(ua, ji, date)) { + if (!select_backups_before_date(ua, rx, date)) { return 0; } done = true; @@ -319,7 +258,7 @@ static int user_select_jobids(UAContext *ua, JOBIDS *ji) return 0; } bstrncpy(date, ua->argv[i], sizeof(date)); - if (!select_backups_before_date(ua, ji, date)) { + if (!select_backups_before_date(ua, rx, date)) { return 0; } done = true; @@ -369,7 +308,7 @@ static int user_select_jobids(UAContext *ua, JOBIDS *ji) if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) { return 0; } - bstrncpy(ji->JobIds, ua->cmd, sizeof(ji->JobIds)); + bstrncpy(rx->JobIds, ua->cmd, sizeof(rx->JobIds)); break; case 3: /* Enter an SQL list command */ if (!get_cmd(ua, _("Enter SQL list command: "))) { @@ -380,7 +319,7 @@ static int user_select_jobids(UAContext *ua, JOBIDS *ji) break; case 4: /* Select the most recent backups */ bstrutime(date, sizeof(date), time(NULL)); - if (!select_backups_before_date(ua, ji, date)) { + if (!select_backups_before_date(ua, rx, date)) { return 0; } break; @@ -397,26 +336,30 @@ static int user_select_jobids(UAContext *ua, JOBIDS *ji) bsendmsg(ua, _("Improper date format.\n")); } bstrncpy(date, ua->cmd, sizeof(date)); - if (!select_backups_before_date(ua, ji, date)) { + if (!select_backups_before_date(ua, rx, date)) { return 0; } - - case 6: /* Cancel or quit */ + break; + case 6: /* Enter files */ + bsendmsg(ua, "Not yet implemented\n"); + return 0; + + case 7: /* Cancel or quit */ return 0; } } - if (*ji->JobIds == 0) { + if (*rx->JobIds == 0) { bsendmsg(ua, _("No Jobs selected.\n")); return 0; } bsendmsg(ua, _("You have selected the following JobId%s: %s\n"), - strchr(ji->JobIds,',')?"s":"",ji->JobIds); + strchr(rx->JobIds,',')?"s":"",rx->JobIds); memset(&jr, 0, sizeof(JOB_DBR)); - ji->TotalFiles = 0; - for (p=ji->JobIds; ; ) { + rx->TotalFiles = 0; + for (p=rx->JobIds; ; ) { int stat = next_jobid_from_list(&p, &JobId); if (stat < 0) { bsendmsg(ua, _("Invalid JobId in list.\n")); @@ -433,16 +376,93 @@ static int user_select_jobids(UAContext *ua, JOBIDS *ji) bsendmsg(ua, _("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db)); return 0; } - ji->TotalFiles += jr.JobFiles; + rx->TotalFiles += jr.JobFiles; } return 1; } +static void build_directory_tree(UAContext *ua, RESTORE_CTX *rx) +{ + TREE_CTX tree; + NAME_LIST name_list; + JobId_t JobId, last_JobId; + char *p; + POOLMEM *query; + char *nofname = ""; + + memset(&tree, 0, sizeof(TREE_CTX)); + memset(&name_list, 0, sizeof(name_list)); + /* + * Build the directory tree containing JobIds user selected + */ + tree.root = new_tree(rx->TotalFiles); + tree.root->fname = nofname; + tree.ua = ua; + query = get_pool_memory(PM_MESSAGE); + last_JobId = 0; + /* + * For display purposes, the same JobId, with different volumes may + * appear more than once, however, we only insert it once. + */ + int items = 0; + for (p=rx->JobIds; next_jobid_from_list(&p, &JobId) > 0; ) { + + if (JobId == last_JobId) { + continue; /* eliminate duplicate JobIds */ + } + last_JobId = JobId; + bsendmsg(ua, _("Building directory tree for JobId %u ...\n"), JobId); + items++; + /* + * Find files for this JobId and insert them in the tree + */ + Mmsg(&query, uar_sel_files, JobId); + if (!db_sql_query(ua->db, query, insert_tree_handler, (void *)&tree)) { + bsendmsg(ua, "%s", db_strerror(ua->db)); + } + /* + * Find the FileSets for this JobId and add to the name_list + */ + Mmsg(&query, uar_mediatype, JobId); + if (!db_sql_query(ua->db, query, unique_name_list_handler, (void *)&name_list)) { + bsendmsg(ua, "%s", db_strerror(ua->db)); + } + } + bsendmsg(ua, "%d Job%s inserted into the tree and marked for extraction.\n", + items, items==1?"":"s"); + free_pool_memory(query); + + /* Check MediaType and select storage that corresponds */ + get_storage_from_mediatype(ua, &name_list, rx); + free_name_list(&name_list); + + if (find_arg(ua, _("all")) < 0) { + /* Let the user select which files to restore */ + user_select_files_from_tree(&tree); + } + + /* + * Walk down through the tree finding all files marked to be + * extracted making a bootstrap file. + */ + for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) { + Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node); + if (node->extract) { + Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex); + add_findex(rx->bsr, node->JobId, node->FileIndex); + rx->selected_files++; + } + } + + free_tree(tree.root); /* free the directory tree */ +} + + /* * This routine is used to get the current backup or a backup * before the specified date. */ -static int select_backups_before_date(UAContext *ua, JOBIDS *ji, char *date) +static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date) { int stat = 0; POOLMEM *query; @@ -469,7 +489,7 @@ static int select_backups_before_date(UAContext *ua, JOBIDS *ji, char *date) if (!get_client_dbr(ua, &cr)) { goto bail_out; } - bstrncpy(ji->ClientName, cr.Name, sizeof(ji->ClientName)); + bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName)); /* * Select FileSet @@ -506,30 +526,30 @@ static int select_backups_before_date(UAContext *ua, JOBIDS *ji, char *date) /* Note, this is needed as I don't seem to get the callback * from the call just above. */ - ji->JobTDate = 0; - if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)ji)) { + rx->JobTDate = 0; + if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) { bsendmsg(ua, "%s\n", db_strerror(ua->db)); } - if (ji->JobTDate == 0) { + if (rx->JobTDate == 0) { bsendmsg(ua, _("No Full backup before %s found.\n"), date); goto bail_out; } /* Now find all Incremental/Decremental Jobs after Full save */ - Mmsg(&query, uar_inc_dec, edit_uint64(ji->JobTDate, ed1), date, + Mmsg(&query, uar_inc_dec, edit_uint64(rx->JobTDate, ed1), date, cr.ClientId, fsr.FileSetId); if (!db_sql_query(ua->db, query, NULL, NULL)) { bsendmsg(ua, "%s\n", db_strerror(ua->db)); } /* Get the JobIds from that list */ - ji->JobIds[0] = 0; - ji->last_jobid[0] = 0; - if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)ji)) { + rx->JobIds[0] = 0; + rx->last_jobid[0] = 0; + if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) { bsendmsg(ua, "%s\n", db_strerror(ua->db)); } - if (ji->JobIds[0] != 0) { + if (rx->JobIds[0] != 0) { /* Display a list of Jobs selected for this restore */ db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST); } else { @@ -573,18 +593,18 @@ static int next_jobid_from_list(char **p, uint32_t *JobId) */ static int jobid_handler(void *ctx, int num_fields, char **row) { - JOBIDS *ji = (JOBIDS *)ctx; + RESTORE_CTX *rx = (RESTORE_CTX *)ctx; - if (strcmp(ji->last_jobid, row[0]) == 0) { + if (strcmp(rx->last_jobid, row[0]) == 0) { return 0; /* duplicate id */ } - bstrncpy(ji->last_jobid, row[0], sizeof(ji->last_jobid)); + bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid)); /* Concatenate a JobId if it does not exceed array size */ - if (strlen(ji->JobIds)+strlen(row[0])+2 < sizeof(ji->JobIds)) { - if (ji->JobIds[0] != 0) { - strcat(ji->JobIds, ","); + if (strlen(rx->JobIds)+strlen(row[0])+2 < sizeof(rx->JobIds)) { + if (rx->JobIds[0] != 0) { + strcat(rx->JobIds, ","); } - strcat(ji->JobIds, row[0]); + strcat(rx->JobIds, row[0]); } return 0; } @@ -595,9 +615,9 @@ static int jobid_handler(void *ctx, int num_fields, char **row) */ static int last_full_handler(void *ctx, int num_fields, char **row) { - JOBIDS *ji = (JOBIDS *)ctx; + RESTORE_CTX *rx = (RESTORE_CTX *)ctx; - ji->JobTDate = strtoll(row[1], NULL, 10); + rx->JobTDate = strtoll(row[1], NULL, 10); return 0; } @@ -673,7 +693,7 @@ static void free_name_list(NAME_LIST *name_list) name_list->num_ids = 0; } -static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, JOBIDS *ji) +static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx) { char name[MAX_NAME_LENGTH]; STORE *store = NULL; @@ -682,13 +702,13 @@ static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, JOBI bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n" "Restore is not possible. The MediaTypes used are:\n")); print_name_list(ua, name_list); - ji->store = select_storage_resource(ua); + rx->store = select_storage_resource(ua); return; } if (name_list->num_ids == 0) { bsendmsg(ua, _("No MediaType found for your JobIds.\n")); - ji->store = select_storage_resource(ua); + rx->store = select_storage_resource(ua); return; } @@ -701,8 +721,8 @@ static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, JOBI } UnlockRes(); do_prompt(ua, _("Storage"), _("Select Storage resource"), name, sizeof(name)); - ji->store = (STORE *)GetResWithName(R_STORAGE, name); - if (!ji->store) { + rx->store = (STORE *)GetResWithName(R_STORAGE, name); + if (!rx->store) { bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n" "MediaType %s, needed by the Jobs you selected.\n" "You will be allowed to select a Storage device later.\n"),