From 490140e6fcffe6d4aa5bdbba769e2a98890c8595 Mon Sep 17 00:00:00 2001 From: Kern Sibbald Date: Thu, 15 Jan 2004 10:44:26 +0000 Subject: [PATCH] Numerous restore tree changes git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@1011 91ce42f0-d328-0410-95d8-f526ca767f89 --- bacula/ChangeLog | 97 +++++++++++++++++- bacula/ReleaseNotes | 3 + bacula/kernstodo | 118 +++++++++++----------- bacula/src/dird/protos.h | 42 ++++---- bacula/src/dird/ua.h | 40 ++++---- bacula/src/dird/ua_restore.c | 37 ++++--- bacula/src/dird/ua_run.c | 64 ++++++------ bacula/src/dird/ua_tree.c | 187 +++++++++++++++++++++++++++++------ bacula/src/version.h | 4 +- 9 files changed, 415 insertions(+), 177 deletions(-) diff --git a/bacula/ChangeLog b/bacula/ChangeLog index ab9da4c99f..943389bad1 100644 --- a/bacula/ChangeLog +++ b/bacula/ChangeLog @@ -1,5 +1,100 @@ -2003-12-xxx Version 1.33 xxNov03 + + +14Jan04 +- Update kernstodo +- Add quit command to restore tree handler. +- Make restore tree handler remember if a hard link is present, and + in doing a mark, only get database entry if there is a hard link. + mark commands thus run at least 2 orders of magnitude faster. +- Add files=xxx field to run command submitted for restore. +- Add yes to restore run command if either yes or run is command line + argument. +- Make "yes" on command line argument skip prompt for modification of + run job. +- Add markdir and unmarkdir -- both affect only the directory in + question and do not do a recursive descent. +- Make tree command automatically mark all higher level directories to + be restored when a directory or a file is selected. Such directories + are indicated by preceding the name with a + to indicate that only + the directory entry is selected and not the whole directory tree. +- Modify a few tree commands to walk through all arguments rather than + just taking the first one. +13Jan04 +- Update copyright date on changed files. +- Make mark command in restore tree run *much* faster by accessing database only + if the file actually is hard linked -- determined at time tree it built by + keeping a has_link bit in the tree entry. +12Jan04 +- Modify findlib/makepath.c to create all parent directories with full permissions. + This should solve the access problems on restoring files on Win32 systems. +- Modify restore to report **** Restore Error **** if any error are found. + I.e. a file could not be created. +- Change a few errors into warnings -- e.g. if permissions could not be set, but + the file is actually restored. +11Jan04 +- Replace bsd_queue with dlist in lib/watchdog.c +- Add Date: header to bsmtp. Thanks to Meno Abels (abels) on SourceForge + for reporting this. +- Remove ap==NULL check in src/lib/var.c as suggested by Christoph Barbian + because ap (va_list) is a struct on the Alpha, so the code does not + compile. +10Jan04 +- Implement first cut of polling for a Volume. New Device directive + "Volume Poll Interval = xxx" where xxx is a time specification + (e.g. 5min). Default is off. +- Call pthread_cancel on SD msgchan only if the threadid is non-zero. +07Jan04 +- Change RH autostart scripts to start in Dan's order. +06Jan04 +- Correct calculation of week of month in scheduler and in scheduled + listing. +04Jan04 +- Fix cancel of job waiting on mount to be a bit cleaner. +03Jan04 +- Fix seg fault -- don't close db in db_open_database(). It must explicitly + be called. +- Doc updates +- Allow purge of a single JobId from a Volume (code untested) +- Implement fix to keep SD from looping complaining about the wrong volume + at the end of a tape -- reported by Phil, and fix confirmed by him. I cannot + seem to reproduce the bug here. +- Clean up a few more strcpy()s. +01Jan04 +- Fix configure.in FD User print per Lars. +- Doc update. +- New bacula.spec.in from Scott. Nice! +- Make RunAfterJob non-fatal if it errs since the Job has really + already completed the save. +- Replace sprintf with bsnprintf in message.c +31Dec03 +- Note, this change affects only the Win32 FD. +- Fixed Win32 FD crash due to missing argument in status command. It + always crashed if there was a job that had previously run. Thanks to + Christopher Hull for finding and diagnosing this problem. +29Dec03 +- Fix SELECT as indicated by Dan in sql_get.c for returning Volume names. +- Fix last_jobs list crash for utility programs (btape, ...). +- Correct editing of FileSet name into SQL command. +- Allow calling watchdog stop without initialization for utility programs. +- Enhance btape test by attempting to forward space past the end of the tape. +- Detect CAP_FASTFSF in btape test as possible reason for failure. +- Attempt to prevent infinite loops in fsf_dev() if device not properly + configured. +- Attempt to get correct file number on tape after backspacing at EOM. The + correction should resolve differences in OS tape driver implementations. +- Add ctl-c detection to bconsole. When reading from tty, interrupts any + output from Bacula, when reading from a file, it terminates the reading + and returns possibly exiting if it is a batch job. +- Create devel_bacula.in script and make it configure itself. Very similar + to the new bacula.in. +- Modify bacula.in to have separate file locations for each daemon -- makes + it easier to make a new devel_bacula.in when there are changes. +- Remove old config files when rebuilding configure to avoid error msgs. +- Fix btape "test" to respect TWO EOF. +- Fix fsf_dev() to use system notion of file if it is valid after bsf_dev(). +- Move btraceback.gdb to scripts directory on installl. +- Add new mandrake bacula.spec.in 28Dec03 - Find commonset of c_iflags that work with FreeBSD tcsetattr on terminals. - Remove comments from string concatenation -- doesn't work on FreeBSD diff --git a/bacula/ReleaseNotes b/bacula/ReleaseNotes index 729c67e3d3..adad977359 100644 --- a/bacula/ReleaseNotes +++ b/bacula/ReleaseNotes @@ -4,6 +4,7 @@ Bacula code: Total files = 281 Total lines = 83,815 (*.h *.c *.in) Most Significant Changes since 1.32d +- Dan Langille has written a PostgreSQL driver for Bacula. - Implement "update slots scan" that reads the volume label(s). - The full form of the scan is "scan=1,2,4-5,7". With no specification, all occupied slots are scanned. @@ -24,6 +25,8 @@ Most Significant Changes since 1.32d tape block, so the tape is not recognized. Other Changes since 1.32d +- Restore directory tree automatically selects all higher level + directories to be restored. - Implement conio.c to use in console program -- mini-readline. - Enhance "fill" command of btape -- simpler output. Use -v to cause last block to be dumped after write and after re-read. diff --git a/bacula/kernstodo b/bacula/kernstodo index a5abaa5860..1932a6f3df 100644 --- a/bacula/kernstodo +++ b/bacula/kernstodo @@ -1,5 +1,5 @@ Kern's ToDo List - 09 January 2004 + 13 January 2004 Documentation to do: (any release a little bit at a time) - Document running a test version. @@ -55,8 +55,13 @@ For 1.33 Testing/Documentation: - Add db check test to regression. Test each function like delete, purge, ... - Add subsections to the Disaster Recovery index section. +- Document Pool keyword for restore. For 1.33 +- Finish code passing files=nnn to restore start. +- Add Console usr permissions -- do by adding regex filters for + jobs, clients, storage, ... +- Put max network buffer size on a directive. - Why does "mark cygwin" take so long!!!!!!!! - When a file is set for restore, walk back up the chain of directories, setting them to be restored. @@ -184,7 +189,6 @@ After 1.33: Job report (Volker Sauer). - Client does not show busy during Estimate command. - Implement Console mtx commands. -- Look at 2Gb limit for SQLite. - Implement 3 Pools for a Job: Job {   Name = ... @@ -196,26 +200,6 @@ After 1.33: GRANT all privileges ON bacula.* TO bacula@localhost IDENTIFIED BY 'bacula_password'; FLUSH PRIVILEGES; -- Define week of year for scheduler. W01, W02, ... - Week 01 of a year is per definition the first week that has the - Thursday in this year, which is equivalent to the week that contains the - fourth day of January. In other words, the first week of a new year is - the week that has the majority of its days in the new year. Week 01 - might also contain days from the previous year and the week before week - 01 of a year is the last week (52 or 53) of the previous year even if it - contains days from the new year. A week starts with Monday (day 1) and - ends with Sunday (day 7). For example, the first week of the year 1997 - lasts from 1996-12-30 to 1997-01-05 and can be written in standard - notation as - - 1997-W01 or 1997W01 - - The week notation can also be extended by a number indicating the day - of the week. For example, the day 1996-12-31, which is the Tuesday (day - 2) of the first week of 1997, can also be written as - - 1997-W01-2 or 1997W012 - - Implement a Mount Command and an Unmount Command where the users could specify a system command to be performed to do the mount, after which Bacula could attempt to @@ -269,7 +253,6 @@ After 1.33: - Take a careful look at Level for the estimate command, maybe make it a command line option. - Add Volume name to "I cannot write on this volume because" -- Make restore job check if all the files are actually restored. - Make tree walk routines like cd, ls, ... more user friendly by handling spaces better. - Write your PID file and chown root:wheel before drop. @@ -298,9 +281,7 @@ After 1.33: - Set IO_NOWAIT on Bacula TCP/IP packets. - Try doing a raw partition backup and restore by mounting a Windows partition. -- Report CVS problems to SourceForge. -- Implement .consolerc for Console -- From Lars Köllers: +- From Lars Kellers: Yes, it would allow to highly automatic the request for new tapes. If a tape is empty, bacula reads the barcodes (native or simulated), and if an unused tape is found, it runs the label command with all the @@ -309,8 +290,6 @@ After 1.33: By the way can bacula automatically "move" an empty/purged volume say in the "short" pool to the "long" pool if this pool runs out of volume space? -- Either restrict the characters in a name, or fix the problem - emailing with names containing / (smtp command line breaks). - Eliminate orphaned jobs: dbcheck, normal pruning, delete job command. Hm.  Well, there are the remaining orphaned job records: @@ -359,14 +338,9 @@ After 1.33: - Walk through the Pool records rather than the Job records in dird.c to create/update pools. - What to do about "list files job=xxx". -- Implement scan: for every slot it finds, zero the slot of - Volume other volume having that slot. - When job rescheduled, status gives is waiting for Client Rufus to connect to Storage File. Dir needs to inform SD that job is rescheduled. -- Fix get_storage_from_media_type (ua_restore) to use command line - storage= -- Don't print "Warning: Wrong Volume mounted ..." if mounting second volume. - Make Dmsg look at global before calling subroutine. - Enable trace output at runtime for Win32 - Available volumes for autochangers (see patrick@baanboard.com 3 Sep 03 @@ -374,7 +348,6 @@ After 1.33: - Get and test MySQL 4.0 - Do a complete audit of all pthreads_mutex, cond, ... to ensure that any that are dynamically initialized are destroyed when no longer used. -- Write a mini-readline with history and editing. - Look at how fuser works and /proc/PID/fd that is how Nic found the file descriptor leak in Bacula. - Implement WrapCounters in Counters. @@ -385,24 +358,7 @@ After 1.33: - Can we dynamically change FileSets? - If pool specified to label command and Label Format is specified, automatically generate the Volume name. -- Take a careful look a the Basic recycling algorithm. When Bacula - chooses, the order should be: - - Look for Append - - Look for Recycle or Purged - - Prune volumes - - Look for purged - Instead of using lowest media Id, find the least recently used - volume. - - When the tape is mounted and Bacula requests the status - - Do everything possible to use it. - - Define a "available" status, which is the currently mounted - Volume and all volumes that are currently in the autochanger. - - 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, ...) @@ -412,10 +368,7 @@ After 1.33: time as the user walks through the tree). - Possibly use the hash code if the user selects all for a restore command. - Orphaned Dir buffer at parse_conf.c:373 => store_dir -- Add Console usr permissions. - Fix "restore all" to bypass building the tree. -- 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) - Implement Release Device in the Job resource to unmount a drive. - Implement Acquire Device in the Job resource to mount a drive, @@ -426,7 +379,6 @@ After 1.33: - Make things like list where a file is saved case independent for Windows. - Implement migrate -- Implement a PostgreSQL driver. - Bacula needs to propagate SD errors. > > cluster-dir: Start Backup JobId 252, Job=REUTERS.2003-08-11_15.04.12 > > prod4-sd: REUTERS.2003-08-11_15.04.12 Error: Write error on device @@ -447,9 +399,7 @@ After 1.33: We must cd into the directory then create the file without the full path name. - lstat() is not going to work on Win32 for testing date. -- Something is not right in last block of fill command. - Implement a Recycle command -- Add FileSet to command line arguments for restore. - Add client name to cram-md5 challenge so Director can immediately verify if it is the correct client. - Add JobLevel in FD status (but make sure it is defined). @@ -508,7 +458,6 @@ After 1.33: - Implement restore "current system", but take all files without doing selection tree -- so that jobs without File records can be restored. -- Implement a relocatable bacula.spec - Add prefixlinks to where or not where absolute links to FD. - Issue message to mount a new tape before the rewind. - Simplified client job initiation for portables. @@ -559,7 +508,6 @@ After 1.33: - Add UA rc and history files. - put termcap (used by console) in ./configure and allow -with-termcap-dir. -- Enhance time and size scanning routines. - Fix Autoprune for Volumes to respect need for full save. - Fix Win32 config file definition name on /install - Compare tape to Client files (attributes, or attributes and data) @@ -794,7 +742,7 @@ Need: ============================================================= - Request For Comments For File Backup Options + Request For Comments For File Backup Options 10 November 2002 Subject: File Backup Options @@ -1000,6 +948,7 @@ Done: (see kernsdone for more) - Implement ClientRunBeforeJob and ClientRunAfterJob. - Implement forward spacing block/file: position_device(bsr) -- just before read_block_from_device(); + === for 1.33 - Change console to bconsole. - Change smtp to bsmtp. @@ -1086,3 +1035,52 @@ Done: (see kernsdone for more) Could I get you to double check the switch () statements in the job_check_maxwaittime and job_check_maxruntime functions in src/dird/job.c? +- Define week of year for scheduler. W01, W02, ... + Week 01 of a year is per definition the first week that has the + Thursday in this year, which is equivalent to the week that contains the + fourth day of January. In other words, the first week of a new year is + the week that has the majority of its days in the new year. Week 01 + might also contain days from the previous year and the week before week + 01 of a year is the last week (52 or 53) of the previous year even if it + contains days from the new year. A week starts with Monday (day 1) and + ends with Sunday (day 7). For example, the first week of the year 1997 + lasts from 1996-12-30 to 1997-01-05 and can be written in standard + notation as + 1997-W01 or 1997W01 + The week notation can also be extended by a number indicating the day + of the week. For example, the day 1996-12-31, which is the Tuesday (day + 2) of the first week of 1997, can also be written as + 1997-W01-2 or 1997W012 +- Either restrict the characters in a name, or fix the problem + emailing with names containing / (smtp command line breaks). +- Implement .consolerc for Console +- Implement scan: for every slot it finds, zero the slot of + Volume other volume having that slot. +- Make restore job check if all the files are actually restored. +- Look at 2Gb limit for SQLite. +- Fix get_storage_from_media_type (ua_restore) to use command line + storage= +- Don't print "Warning: Wrong Volume mounted ..." if mounting second volume. +- Write a mini-readline with history and editing. +- Take a careful look a the Basic recycling algorithm. When Bacula + chooses, the order should be: + - Look for Append + - Look for Recycle or Purged + - Prune volumes + - Look for purged + Instead of using lowest media Id, find the least recently used + volume. + + When the tape is mounted and Bacula requests the status + - Do everything possible to use it. + + Define a "available" status, which is the currently mounted + Volume and all volumes that are currently in the autochanger. +- Is a pool specification really needed for a restore? Yes, and + you may want to exclude archive Pools. +- Implement a PostgreSQL driver. +- Fix restore to list errors if Invalid block found, and if # files + restored does not match # expected. +- Something is not right in last block of fill command. +- Add FileSet to command line arguments for restore. +- Enhance time and size scanning routines. diff --git a/bacula/src/dird/protos.h b/bacula/src/dird/protos.h index d9a9925525..a3b9e596dc 100644 --- a/bacula/src/dird/protos.h +++ b/bacula/src/dird/protos.h @@ -62,7 +62,7 @@ int variable_expansion(JCR *jcr, char *inp, POOLMEM **exp); /* fd_cmds.c */ extern int connect_to_file_daemon(JCR *jcr, int retry_interval, - int max_retry_time, int verbose); + int max_retry_time, int verbose); extern int send_include_list(JCR *jcr); extern int send_exclude_list(JCR *jcr); extern int send_bootstrap_file(JCR *jcr); @@ -70,7 +70,7 @@ extern int send_level_command(JCR *jcr); extern int get_attributes_and_put_in_catalog(JCR *jcr); extern int get_attributes_and_compare_to_catalog(JCR *jcr, JobId_t JobId); extern int put_file_into_catalog(JCR *jcr, long file_index, char *fname, - char *link, char *attr, int stream); + char *link, char *attr, int stream); extern void get_level_since_time(JCR *jcr, char *since, int since_len); extern int send_run_before_and_after_commands(JCR *jcr); @@ -94,7 +94,7 @@ extern void mount_request(JCR *jcr, BSOCK *bs, char *buf); /* msgchan.c */ extern int connect_to_storage_daemon(JCR *jcr, int retry_interval, - int max_retry_time, int verbose); + int max_retry_time, int verbose); extern int start_storage_daemon_job(JCR *jcr); extern int start_storage_daemon_message_thread(JCR *jcr); extern int bget_dirmsg(BSOCK *bs); @@ -142,28 +142,28 @@ JCR *create_control_jcr(char *base_name, int job_type); void free_ua_context(UAContext *ua); /* ua_select.c */ -STORE *select_storage_resource(UAContext *ua); -JOB *select_job_resource(UAContext *ua); -JOB *select_restore_job_resource(UAContext *ua); -CLIENT *select_client_resource(UAContext *ua); +STORE *select_storage_resource(UAContext *ua); +JOB *select_job_resource(UAContext *ua); +JOB *select_restore_job_resource(UAContext *ua); +CLIENT *select_client_resource(UAContext *ua); FILESET *select_fileset_resource(UAContext *ua); -int select_pool_and_media_dbr(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr); -int select_media_dbr(UAContext *ua, MEDIA_DBR *mr); -int select_pool_dbr(UAContext *ua, POOL_DBR *pr); -int select_client_dbr(UAContext *ua, CLIENT_DBR *cr); - -void start_prompt(UAContext *ua, char *msg); -void add_prompt(UAContext *ua, char *prompt); -int do_prompt(UAContext *ua, char *automsg, char *msg, char *prompt, int max_prompt); -CAT *get_catalog_resource(UAContext *ua); +int select_pool_and_media_dbr(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr); +int select_media_dbr(UAContext *ua, MEDIA_DBR *mr); +int select_pool_dbr(UAContext *ua, POOL_DBR *pr); +int select_client_dbr(UAContext *ua, CLIENT_DBR *cr); + +void start_prompt(UAContext *ua, char *msg); +void add_prompt(UAContext *ua, char *prompt); +int do_prompt(UAContext *ua, char *automsg, char *msg, char *prompt, int max_prompt); +CAT *get_catalog_resource(UAContext *ua); STORE *get_storage_resource(UAContext *ua, int use_default); -int get_media_type(UAContext *ua, char *MediaType, int max_media); -int get_pool_dbr(UAContext *ua, POOL_DBR *pr); -int get_client_dbr(UAContext *ua, CLIENT_DBR *cr); +int get_media_type(UAContext *ua, char *MediaType, int max_media); +int get_pool_dbr(UAContext *ua, POOL_DBR *pr); +int get_client_dbr(UAContext *ua, CLIENT_DBR *cr); POOL *get_pool_resource(UAContext *ua); POOL *select_pool_resource(UAContext *ua); CLIENT *get_client_resource(UAContext *ua); -int get_job_dbr(UAContext *ua, JOB_DBR *jr); +int get_job_dbr(UAContext *ua, JOB_DBR *jr); int find_arg_keyword(UAContext *ua, char **list); int find_arg(UAContext *ua, char *keyword); @@ -172,7 +172,7 @@ int do_keyword_prompt(UAContext *ua, char *msg, char **list); int confirm_retention(UAContext *ua, utime_t *ret, char *msg); /* ua_tree.c */ -void user_select_files_from_tree(TREE_CTX *tree); +bool user_select_files_from_tree(TREE_CTX *tree); int insert_tree_handler(void *ctx, int num_fields, char **row); /* ua_prune.c */ diff --git a/bacula/src/dird/ua.h b/bacula/src/dird/ua.h index d63e949f90..923a8c288c 100644 --- a/bacula/src/dird/ua.h +++ b/bacula/src/dird/ua.h @@ -35,29 +35,29 @@ struct UAContext { JCR *jcr; B_DB *db; CAT *catalog; - POOLMEM *cmd; /* return command/name buffer */ - POOLMEM *args; /* command line arguments */ - char *argk[MAX_CMD_ARGS]; /* argument keywords */ - char *argv[MAX_CMD_ARGS]; /* argument values */ - int argc; /* number of arguments */ - char **prompt; /* list of prompts */ - int max_prompts; /* max size of list */ - int num_prompts; /* current number in list */ - int auto_display_messages; /* if set, display messages */ - int user_notified_msg_pending; /* set when user notified */ - int automount; /* if set, mount after label */ - int quit; /* if set, quit */ - int verbose; /* set for normal UA verbosity */ - uint32_t pint32_val; /* positive integer */ - int32_t int32_val; /* positive/negative */ -}; + POOLMEM *cmd; /* return command/name buffer */ + POOLMEM *args; /* command line arguments */ + char *argk[MAX_CMD_ARGS]; /* argument keywords */ + char *argv[MAX_CMD_ARGS]; /* argument values */ + int argc; /* number of arguments */ + char **prompt; /* list of prompts */ + int max_prompts; /* max size of list */ + int num_prompts; /* current number in list */ + bool auto_display_messages; /* if set, display messages */ + bool user_notified_msg_pending; /* set when user notified */ + bool automount; /* if set, mount after label */ + bool quit; /* if set, quit */ + bool verbose; /* set for normal UA verbosity */ + uint32_t pint32_val; /* positive integer */ + int32_t int32_val; /* positive/negative */ +}; /* Context for insert_tree_handler() */ struct TREE_CTX { - TREE_ROOT *root; /* root */ - TREE_NODE *node; /* current node */ - TREE_NODE *avail_node; /* unused node last insert */ - int cnt; /* count for user feedback */ + TREE_ROOT *root; /* root */ + TREE_NODE *node; /* current node */ + TREE_NODE *avail_node; /* unused node last insert */ + int cnt; /* count for user feedback */ UAContext *ua; }; diff --git a/bacula/src/dird/ua_restore.c b/bacula/src/dird/ua_restore.c index 9b4d29a262..75d8277cb7 100644 --- a/bacula/src/dird/ua_restore.c +++ b/bacula/src/dird/ua_restore.c @@ -98,7 +98,7 @@ 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, 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); +static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx); static void free_rx(RESTORE_CTX *rx); static void split_path_and_filename(RESTORE_CTX *rx, char *fname); static int jobid_fileindex_handler(void *ctx, int num_fields, char **row); @@ -163,7 +163,10 @@ int restore_cmd(UAContext *ua, char *cmd) case 0: goto bail_out; case 1: /* select by jobid */ - build_directory_tree(ua, &rx); + if (!build_directory_tree(ua, &rx)) { + bsendmsg(ua, _("Restore not done.\n")); + goto bail_out; + } break; case 2: /* select by filename, no tree needed */ break; @@ -201,17 +204,17 @@ int restore_cmd(UAContext *ua, char *cmd) if (rx.where) { Mmsg(&ua->cmd, "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\"" - " where=\"%s\"", + " where=\"%s\" files=%d", job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"", - working_directory, rx.where); + working_directory, rx.where, rx.selected_files); } else { Mmsg(&ua->cmd, "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\"", job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"", working_directory); } - if (find_arg(ua, _("run")) >= 0) { - pm_strcat(&ua->cmd, " run"); /* pass it on to the run command */ + if (find_arg(ua, _("run")) >= 0 || find_arg(ua, _("yes"))) { + pm_strcat(&ua->cmd, " yes"); /* pass it on to the run command */ } Dmsg1(400, "Submitting: %s\n", ua->cmd); parse_ua_args(ua); @@ -658,12 +661,13 @@ static void split_path_and_filename(RESTORE_CTX *rx, char *name) Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname); } -static void build_directory_tree(UAContext *ua, RESTORE_CTX *rx) +static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx) { TREE_CTX tree; JobId_t JobId, last_JobId; char *p; char *nofname = ""; + bool OK = true; memset(&tree, 0, sizeof(TREE_CTX)); /* @@ -708,24 +712,27 @@ static void build_directory_tree(UAContext *ua, RESTORE_CTX *rx) get_storage_from_mediatype(ua, &rx->name_list, rx); if (find_arg(ua, _("all")) < 0) { - /* Let the user select which files to restore */ - user_select_files_from_tree(&tree); + /* Let the user interact in selecting which files to restore */ + OK = 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++; + if (OK) { + 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 || node->extract_dir) { + 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 */ + return OK; } diff --git a/bacula/src/dird/ua_run.c b/bacula/src/dird/ua_run.c index d8c96b821e..05113cc2b9 100644 --- a/bacula/src/dird/ua_run.c +++ b/bacula/src/dird/ua_run.c @@ -75,12 +75,10 @@ int run_cmd(UAContext *ua, char *cmd) N_("when"), /* 12 */ N_("priority"), /* 13 */ N_("yes"), /* 14 -- if you change this change YES_POS too */ - N_("run"), /* 15 -- if you change this change RUN_POS too */ N_("verifyjob"), /* 16 */ NULL}; #define YES_POS 14 -#define RUN_POS 15 if (!open_db(ua)) { return 1; @@ -105,7 +103,7 @@ int run_cmd(UAContext *ua, char *cmd) for (j=0; !found && kw[j]; j++) { if (strcasecmp(ua->argk[i], _(kw[j])) == 0) { /* Note, yes and run have no value, so do not err */ - if (!ua->argv[i] && (j != YES_POS /*yes*/ && j != RUN_POS)) { + if (!ua->argv[i] && j != YES_POS /*yes*/) { bsendmsg(ua, _("Value missing for keyword %s\n"), ua->argk[i]); return 1; } @@ -213,10 +211,9 @@ int run_cmd(UAContext *ua, char *cmd) } break; case 14: /* yes */ - case 15: /* run */ found = true; break; - case 16: /* Verify Job */ + case 15: /* Verify Job */ if (verify_job_name) { bsendmsg(ua, _("Verify Job specified twice.\n")); return 1; @@ -386,6 +383,39 @@ try_again: replace = ReplaceOptions[i].name; } } + if (level_name) { + /* Look up level name and pull code */ + found = 0; + for (i=0; joblevels[i].level_name; i++) { + if (strcasecmp(level_name, _(joblevels[i].level_name)) == 0) { + jcr->JobLevel = joblevels[i].level; + found = 1; + break; + } + } + if (!found) { + bsendmsg(ua, _("Level %s not valid.\n"), level_name); + goto bail_out; + } + } + level_name = NULL; + if (jid) { + jcr->RestoreJobId = atoi(jid); + } + + /* Run without prompting? */ + if (find_arg(ua, _("yes")) > 0) { + Dmsg1(200, "Calling run_job job=%x\n", jcr->job); + run_job(jcr); + free_jcr(jcr); /* release jcr */ + bsendmsg(ua, _("Run command submitted.\n")); + return 1; + } + + /* + * Prompt User to see if all run job parameters are correct, and + * allow him to modify them. + */ Dmsg1(20, "JobType=%c\n", jcr->JobType); switch (jcr->JobType) { char ec1[30]; @@ -409,22 +439,6 @@ Priority: %d\n"), break; case JT_BACKUP: case JT_VERIFY: - if (level_name) { - /* Look up level name and pull code */ - found = 0; - for (i=0; joblevels[i].level_name; i++) { - if (strcasecmp(level_name, _(joblevels[i].level_name)) == 0) { - jcr->JobLevel = joblevels[i].level; - found = 1; - break; - } - } - if (!found) { - bsendmsg(ua, _("Level %s not valid.\n"), level_name); - goto bail_out; - } - } - level_name = NULL; if (jcr->JobType == JT_BACKUP) { bsendmsg(ua, _("Run %s job\n\ JobName: %s\n\ @@ -535,14 +549,6 @@ Priority: %d\n"), goto bail_out; } - /* Run without prompting? */ - if (find_arg(ua, _("yes")) > 0) { - Dmsg1(200, "Calling run_job job=%x\n", jcr->job); - run_job(jcr); - free_jcr(jcr); /* release jcr */ - bsendmsg(ua, _("Run command submitted.\n")); - return 1; - } if (!get_cmd(ua, _("OK to run? (yes/mod/no): "))) { goto bail_out; diff --git a/bacula/src/dird/ua_tree.c b/bacula/src/dird/ua_tree.c index 93a8f8a218..b7641f9b3c 100644 --- a/bacula/src/dird/ua_tree.c +++ b/bacula/src/dird/ua_tree.c @@ -38,34 +38,40 @@ /* Forward referenced commands */ static int markcmd(UAContext *ua, TREE_CTX *tree); +static int markdircmd(UAContext *ua, TREE_CTX *tree); 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 lsmark(UAContext *ua, TREE_CTX *tree); +static int lsmarkcmd(UAContext *ua, TREE_CTX *tree); static int 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); static int pwdcmd(UAContext *ua, TREE_CTX *tree); static int unmarkcmd(UAContext *ua, TREE_CTX *tree); +static int unmarkdircmd(UAContext *ua, TREE_CTX *tree); 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; }; static struct cmdstruct commands[] = { { N_("cd"), cdcmd, _("change current directory")}, - { N_("count"), countcmd, _("count marked files")}, + { N_("count"), countcmd, _("count marked files in and below the cd")}, { N_("dir"), dircmd, _("list current directory")}, - { N_("done"), quitcmd, _("leave file selection mode")}, + { N_("done"), donecmd, _("leave file selection mode")}, { N_("estimate"), estimatecmd, _("estimate restore size")}, - { N_("exit"), quitcmd, _("exit = done")}, - { N_("find"), findcmd, _("find files")}, + { N_("exit"), donecmd, _("exit = done")}, + { N_("find"), findcmd, _("find files -- wildcards allowed")}, { N_("help"), helpcmd, _("print help")}, - { N_("lsmark"), lsmark, _("list the marked files")}, - { N_("ls"), lscmd, _("list current directory")}, + { N_("lsmark"), lsmarkcmd, _("list the marked files in and below the cd")}, + { N_("ls"), lscmd, _("list current directory -- wildcards allowed")}, { 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")}, }; #define comsize (sizeof(commands)/sizeof(struct cmdstruct)) @@ -76,9 +82,10 @@ static struct cmdstruct commands[] = { * files to be restored. This is sort of like a mini-shell * that allows "cd", "pwd", "add", "rm", ... */ -void user_select_files_from_tree(TREE_CTX *tree) +bool user_select_files_from_tree(TREE_CTX *tree) { char cwd[2000]; + bool stat; /* Get a new context so we don't destroy restore command args */ UAContext *ua = new_ua_context(tree->ua->jcr); ua->UA_sock = tree->ua->UA_sock; /* patch in UA socket */ @@ -95,7 +102,7 @@ void user_select_files_from_tree(TREE_CTX *tree) tree_getpath(tree->node, cwd, sizeof(cwd)); bsendmsg(tree->ua, _("cwd is: %s\n"), cwd); for ( ;; ) { - int found, len, stat, i; + int found, len, i; if (!get_cmd(ua, "$ ")) { break; } @@ -106,7 +113,7 @@ void user_select_files_from_tree(TREE_CTX *tree) len = strlen(ua->argk[0]); found = 0; - stat = 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 */ @@ -122,7 +129,10 @@ void user_select_files_from_tree(TREE_CTX *tree) } } 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 */ + return stat; } @@ -173,7 +183,9 @@ int insert_tree_handler(void *ctx, int num_fields, char **row) new_node->JobId = (JobId_t)str_to_int64(row[3]); new_node->type = type; new_node->extract = true; /* extract all by default */ - new_node->extract_dir = true; /* if dir, extract it */ + if (type == TN_DIR || type == TN_DIR_NLS) { + new_node->extract_dir = true; /* if dir, extract it */ + } new_node->have_link = (decode_LinkFI(row[4]) != 0); tree->cnt++; return 0; @@ -193,14 +205,28 @@ static int set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, bool extr int count = 0; node->extract = extract; + if (node->type == TN_DIR || node->type == TN_DIR_NLS) { + node->extract_dir = extract; /* set/clear dir too */ + } if (node->type != TN_NEWDIR) { count++; } /* For a non-file (i.e. directory), we see all the children */ if (node->type != TN_FILE) { + /* Recursive set children within directory */ for (n=node->child; n; n=n->sibling) { 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; + } + } } else if (extract) { char cwd[2000]; /* @@ -223,6 +249,9 @@ static int set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, bool extr 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; } } @@ -232,6 +261,10 @@ static int set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, bool extr return count; } +/* + * 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; @@ -256,6 +289,34 @@ static int markcmd(UAContext *ua, TREE_CTX *tree) return 1; } +static int markdircmd(UAContext *ua, TREE_CTX *tree) +{ + TREE_NODE *node; + int count = 0; + + if (ua->argc < 2 || !tree->node->child) { + 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++; + } + } + } + } + if (count == 0) { + bsendmsg(ua, _("No directories marked.\n")); + } else { + bsendmsg(ua, _("%d director%s marked.\n"), count, count==1?"y":"ies"); + } + return 1; +} + + static int countcmd(UAContext *ua, TREE_CTX *tree) { int total, num_extract; @@ -264,12 +325,12 @@ static int countcmd(UAContext *ua, TREE_CTX *tree) for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) { if (node->type != TN_NEWDIR) { total++; - if (node->extract) { + if (node->extract || node->extract_dir) { num_extract++; } } } - bsendmsg(ua, "%d total files. %d marked to be restored.\n", total, num_extract); + bsendmsg(ua, "%d total files/dirs. %d marked to be restored.\n", total, num_extract); return 1; } @@ -285,8 +346,16 @@ static int findcmd(UAContext *ua, TREE_CTX *tree) 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)); - bsendmsg(ua, "%s%s\n", node->extract?"*":"", cwd); + if (node->extract) { + tag = "*"; + } else if (node->extract_dir) { + tag = "+"; + } else { + tag = ""; + } + bsendmsg(ua, "%s%s\n", tag, cwd); } } } @@ -304,7 +373,15 @@ static int lscmd(UAContext *ua, TREE_CTX *tree) } for (node = tree->node->child; node; node=node->sibling) { if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) { - bsendmsg(ua, "%s%s%s\n", node->extract?"*":"", node->fname, + char *tag; + if (node->extract) { + tag = "*"; + } else if (node->extract_dir) { + tag = "+"; + } else { + tag = ""; + } + bsendmsg(ua, "%s%s%s\n", tag, node->fname, (node->type==TN_DIR||node->type==TN_NEWDIR)?"/":""); } } @@ -314,7 +391,7 @@ static int lscmd(UAContext *ua, TREE_CTX *tree) /* * Ls command that lists only the marked files */ -static int lsmark(UAContext *ua, TREE_CTX *tree) +static int lsmarkcmd(UAContext *ua, TREE_CTX *tree) { TREE_NODE *node; @@ -322,9 +399,17 @@ static int lsmark(UAContext *ua, TREE_CTX *tree) return 1; } for (node = tree->node->child; node; node=node->sibling) { - if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0 && - node->extract) { - bsendmsg(ua, "%s%s%s\n", node->extract?"*":"", node->fname, + if ((ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) && + (node->extract || node->extract_dir)) { + char *tag; + if (node->extract) { + tag = "*"; + } else if (node->extract_dir) { + tag = "+"; + } else { + tag = ""; + } + bsendmsg(ua, "%s%s%s\n", tag, node->fname, (node->type==TN_DIR||node->type==TN_NEWDIR)?"/":""); } } @@ -338,7 +423,7 @@ extern char *getgroup(gid_t gid); /* * This is actually the long form used for "dir" */ -static void ls_output(char *buf, char *fname, bool extract, struct stat *statp) +static void ls_output(char *buf, char *fname, char *tag, struct stat *statp) { char *p, *f; char ec1[30]; @@ -353,13 +438,10 @@ static void ls_output(char *buf, char *fname, bool extract, struct stat *statp) p += n; p = encode_time(statp->st_ctime, p); *p++ = ' '; - if (extract) { - *p++ = '*'; - } else { - *p++ = ' '; - } - for (f=fname; *f; ) + *p++ = *tag; + for (f=fname; *f; ) { *p++ = *f++; + } *p = 0; } @@ -379,18 +461,29 @@ static int dircmd(UAContext *ua, TREE_CTX *tree) return 1; } for (node = tree->node->child; node; node=node->sibling) { + char *tag; if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) { + if (node->extract) { + tag = "*"; + } else if (node->extract_dir) { + tag = "+"; + } else { + tag = " "; + } 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 */ - ls_output(buf, cwd, node->extract, &statp); + ls_output(buf, cwd, tag, &statp); bsendmsg(ua, "%s\n", buf); } else { /* Something went wrong getting attributes -- print name */ - bsendmsg(ua, "%s%s%s\n", node->extract?"*":"", node->fname, + if (*tag == ' ') { + tag = ""; + } + bsendmsg(ua, "%s%s%s\n", tag, node->fname, (node->type==TN_DIR||node->type==TN_NEWDIR)?"/":""); } } @@ -426,7 +519,7 @@ static int estimatecmd(UAContext *ua, TREE_CTX *tree) } } /* Directory, count only */ - } else if (node->extract) { + } else if (node->extract || node->extract_dir) { num_extract++; } } @@ -518,7 +611,43 @@ static int unmarkcmd(UAContext *ua, TREE_CTX *tree) return 1; } +static int unmarkdircmd(UAContext *ua, TREE_CTX *tree) +{ + TREE_NODE *node; + int count = 0; + + if (ua->argc < 2 || !tree->node->child) { + 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++; + } + } + } + } + + if (count == 0) { + bsendmsg(ua, _("No directories unmarked.\n")); + } else { + bsendmsg(ua, _("%d director%s unmarked.\n"), count, count==1?"y":"ies"); + } + return 1; +} + + +static int donecmd(UAContext *ua, TREE_CTX *tree) +{ + return 0; +} + static int quitcmd(UAContext *ua, TREE_CTX *tree) { + ua->quit = true; return 0; } diff --git a/bacula/src/version.h b/bacula/src/version.h index 244a2b3052..8e793cbb11 100644 --- a/bacula/src/version.h +++ b/bacula/src/version.h @@ -2,8 +2,8 @@ #undef VERSION #define VERSION "1.33" #define VSTRING "1" -#define BDATE "12 Jan 2004" -#define LSMDATE "12Jan04" +#define BDATE "14 Jan 2004" +#define LSMDATE "14Jan04" /* Debug flags */ #undef DEBUG -- 2.39.5