3 * Bacula Director -- User Agent Database restore Command
4 * Creates a bootstrap file for restoring files and
5 * starts the restore job.
7 * Tree handling routines split into ua_tree.c July MMIII.
8 * BSR (bootstrap record) handling routines split into
11 * Kern Sibbald, July MMII
17 Copyright (C) 2002-2003 Kern Sibbald and John Walker
19 This program is free software; you can redistribute it and/or
20 modify it under the terms of the GNU General Public License as
21 published by the Free Software Foundation; either version 2 of
22 the License, or (at your option) any later version.
24 This program is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 General Public License for more details.
29 You should have received a copy of the GNU General Public
30 License along with this program; if not, write to the Free
31 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
40 /* Imported functions */
41 extern int runcmd(UAContext *ua, char *cmd);
43 /* Imported variables */
44 extern char *uar_list_jobs, *uar_file, *uar_sel_files;
45 extern char *uar_del_temp, *uar_del_temp1, *uar_create_temp;
46 extern char *uar_create_temp1, *uar_last_full, *uar_full;
47 extern char *uar_inc_dec, *uar_list_temp, *uar_sel_jobid_temp;
48 extern char *uar_sel_all_temp1, *uar_sel_fileset, *uar_mediatype;
49 extern char *uar_jobid_fileindex;
53 char **name; /* list of names */
54 int num_ids; /* ids stored */
55 int max_ids; /* size of array */
56 int num_del; /* number deleted */
57 int tot_ids; /* total to process */
61 /* Main structure for obtaining JobIds or Files to be restored */
66 char ClientName[MAX_NAME_LENGTH];
68 POOLMEM *JobIds; /* User entered string of JobIds */
72 uint32_t selected_files;
85 #define MAX_ID_LIST_LEN 1000000
88 /* Forward referenced functions */
89 static int last_full_handler(void *ctx, int num_fields, char **row);
90 static int jobid_handler(void *ctx, int num_fields, char **row);
91 static int next_jobid_from_list(char **p, uint32_t *JobId);
92 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
93 static int fileset_handler(void *ctx, int num_fields, char **row);
94 static void print_name_list(UAContext *ua, NAME_LIST *name_list);
95 static int unique_name_list_handler(void *ctx, int num_fields, char **row);
96 static void free_name_list(NAME_LIST *name_list);
97 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx);
98 static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
99 static void build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
100 static void free_rx(RESTORE_CTX *rx);
101 static void split_path_and_filename(RESTORE_CTX *rx, char *fname);
102 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
103 static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file);
104 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx);
105 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
111 int restorecmd(UAContext *ua, char *cmd)
113 RESTORE_CTX rx; /* restore context */
117 memset(&rx, 0, sizeof(rx));
119 rx.path = get_pool_memory(PM_FNAME);
120 rx.fname = get_pool_memory(PM_FNAME);
121 rx.JobIds = get_pool_memory(PM_FNAME);
122 rx.query = get_pool_memory(PM_FNAME);
125 i = find_arg_with_value(ua, "where");
127 rx.where = ua->argv[i];
135 /* Ensure there is at least one Restore Job */
137 while ( (job = (JOB *)GetNextRes(R_JOB, (RES *)job)) ) {
138 if (job->JobType == JT_RESTORE) {
139 if (!rx.restore_job) {
140 rx.restore_job = job;
146 if (!rx.restore_jobs) {
148 "No Restore Job Resource found. You must create at least\n"
149 "one before running this command.\n"));
155 * Request user to select JobIds or files by various different methods
156 * last 20 jobs, where File saved, most recent backup, ...
157 * In the end, a list of files are pumped into
160 switch (user_select_jobids_or_files(ua, &rx)) {
163 return 0; /* error */
164 case 1: /* select by jobid */
165 build_directory_tree(ua, &rx);
172 if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
173 bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
177 // print_bsr(ua, rx.bsr);
178 write_bsr_file(ua, rx.bsr);
179 bsendmsg(ua, _("\n%u file%s selected to restore.\n\n"), rx.selected_files,
180 rx.selected_files==1?"":"s");
182 bsendmsg(ua, _("No files selected to restore.\n"));
187 if (rx.restore_jobs == 1) {
188 job = rx.restore_job;
190 job = select_restore_job_resource(ua);
193 bsendmsg(ua, _("No Restore Job resource found!\n"));
198 get_client_name(ua, &rx);
200 /* Build run command */
203 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
205 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
206 working_directory, rx.where);
209 "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\"",
210 job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
214 Dmsg1(400, "Submitting: %s\n", ua->cmd);
218 bsendmsg(ua, _("Restore command done.\n"));
223 static void free_rx(RESTORE_CTX *rx)
227 free_pool_memory(rx->JobIds);
231 free_pool_memory(rx->fname);
235 free_pool_memory(rx->path);
239 free_pool_memory(rx->query);
242 free_name_list(&rx->name_list);
245 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
247 /* If no client name specified yet, get it now */
248 if (!rx->ClientName[0]) {
250 /* try command line argument */
251 int i = find_arg_with_value(ua, _("client"));
253 bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
256 memset(&cr, 0, sizeof(cr));
257 if (!get_client_dbr(ua, &cr)) {
261 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
267 * The first step in the restore process is for the user to
268 * select a list of JobIds from which he will subsequently
269 * select which files are to be restored.
271 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
274 char date[MAX_TIME_LENGTH];
280 "List last 20 Jobs run",
281 "List Jobs where a given File is saved",
282 "Enter list of JobIds to select",
283 "Enter SQL list command",
284 "Select the most recent backup for a client",
285 "Select backup for a client before a specified time",
286 "Enter a list of files to restore",
298 switch (find_arg_keyword(ua, kw)) {
300 i = find_arg_with_value(ua, _("jobid"));
304 pm_strcpy(&rx->JobIds, ua->argv[i]);
307 case 1: /* current */
308 bstrutime(date, sizeof(date), time(NULL));
309 if (!select_backups_before_date(ua, rx, date)) {
315 i = find_arg_with_value(ua, _("before"));
319 if (str_to_utime(ua->argv[i]) == 0) {
320 bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
323 bstrncpy(date, ua->argv[i], sizeof(date));
324 if (!select_backups_before_date(ua, rx, date)) {
330 if (!get_client_name(ua, rx)) {
334 i = find_arg_with_value(ua, _("file"));
338 pm_strcpy(&ua->cmd, ua->argv[i]);
339 insert_one_file(ua, rx);
342 /* Check MediaType and select storage that corresponds */
343 get_storage_from_mediatype(ua, &rx->name_list, rx);
350 bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n"
351 "to be restored. You will be presented several methods\n"
352 "of specifying the JobIds. Then you will be allowed to\n"
353 "select which files from those JobIds are to be restored.\n\n"));
356 /* If choice not already made above, prompt */
361 start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
362 for (int i=0; list[i]; i++) {
363 add_prompt(ua, list[i]);
366 switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
369 case 0: /* list last 20 Jobs run */
370 db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
373 case 1: /* list where a file is saved */
374 if (!get_cmd(ua, _("Enter Filename: "))) {
377 len = strlen(ua->cmd);
378 fname = (char *)malloc(len * 2 + 1);
379 db_escape_string(fname, ua->cmd, len);
380 Mmsg(&rx->query, uar_file, fname);
382 db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
385 case 2: /* enter a list of JobIds */
386 if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
389 pm_strcpy(&rx->JobIds, ua->cmd);
391 case 3: /* Enter an SQL list command */
392 if (!get_cmd(ua, _("Enter SQL list command: "))) {
395 db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
398 case 4: /* Select the most recent backups */
399 bstrutime(date, sizeof(date), time(NULL));
400 if (!select_backups_before_date(ua, rx, date)) {
404 case 5: /* select backup at specified time */
405 bsendmsg(ua, _("The restored files will the most current backup\n"
406 "BEFORE the date you specify below.\n\n"));
408 if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
411 if (str_to_utime(ua->cmd) != 0) {
414 bsendmsg(ua, _("Improper date format.\n"));
416 bstrncpy(date, ua->cmd, sizeof(date));
417 if (!select_backups_before_date(ua, rx, date)) {
421 case 6: /* Enter files */
422 if (!get_client_name(ua, rx)) {
426 if (!get_cmd(ua, _("Enter filename: "))) {
429 len = strlen(ua->cmd);
433 insert_one_file(ua, rx);
435 /* Check MediaType and select storage that corresponds */
436 get_storage_from_mediatype(ua, &rx->name_list, rx);
439 case 7: /* Cancel or quit */
444 if (*rx->JobIds == 0) {
445 bsendmsg(ua, _("No Jobs selected.\n"));
448 bsendmsg(ua, _("You have selected the following JobId%s: %s\n"),
449 strchr(rx->JobIds,',')?"s":"",rx->JobIds);
451 memset(&jr, 0, sizeof(JOB_DBR));
454 for (p=rx->JobIds; ; ) {
455 int stat = next_jobid_from_list(&p, &JobId);
457 bsendmsg(ua, _("Invalid JobId in list.\n"));
463 if (jr.JobId == JobId) {
464 continue; /* duplicate of last JobId */
467 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
468 bsendmsg(ua, _("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db));
471 rx->TotalFiles += jr.JobFiles;
476 static void insert_one_file(UAContext *ua, RESTORE_CTX *rx)
486 if ((ffd = fopen(p, "r")) == NULL) {
487 bsendmsg(ua, _("Cannot open file %s: ERR=%s\n"),
491 while (fgets(file, sizeof(file), ffd)) {
493 if (!insert_file_into_findex_list(ua, rx, file)) {
494 bsendmsg(ua, _("Error occurred on line %d of %s\n"), line, p);
500 insert_file_into_findex_list(ua, rx, ua->cmd);
506 * For a given file (path+filename), split into path and file, then
507 * lookup the most recent backup in the catalog to get the JobId
508 * and FileIndex, then insert them into the findex list.
510 static int insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file)
512 strip_trailing_junk(file);
513 split_path_and_filename(rx, file);
514 Mmsg(&rx->query, uar_jobid_fileindex, rx->path, rx->fname, rx->ClientName);
516 if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
517 bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
518 rx->query, db_strerror(ua->db));
521 bsendmsg(ua, _("No database record found for: %s\n"), file);
524 rx->selected_files++;
526 * Find the FileSets for this JobId and add to the name_list
528 Mmsg(&rx->query, uar_mediatype, rx->JobId);
529 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
530 bsendmsg(ua, "%s", db_strerror(ua->db));
536 static void split_path_and_filename(RESTORE_CTX *rx, char *name)
540 /* Find path without the filename.
541 * I.e. everything after the last / is a "filename".
542 * OK, maybe it is a directory name, but we treat it like
543 * a filename. If we don't find a / then the whole name
544 * must be a path name (e.g. c:).
546 for (p=f=name; *p; p++) {
548 f = p; /* set pos of last slash */
551 if (*f == '/') { /* did we find a slash? */
552 f++; /* yes, point to filename */
553 } else { /* no, whole thing must be path name */
557 /* If filename doesn't exist (i.e. root directory), we
558 * simply create a blank name consisting of a single
559 * space. This makes handling zero length filenames
564 rx->fname = check_pool_memory_size(rx->fname, rx->fnl+1);
565 memcpy(rx->fname, f, rx->fnl); /* copy filename */
566 rx->fname[rx->fnl] = 0;
568 rx->fname[0] = ' '; /* blank filename */
575 rx->path = check_pool_memory_size(rx->path, rx->pnl+1);
576 memcpy(rx->path, name, rx->pnl);
577 rx->path[rx->pnl] = 0;
584 Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
587 static void build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
590 JobId_t JobId, last_JobId;
594 memset(&tree, 0, sizeof(TREE_CTX));
596 * Build the directory tree containing JobIds user selected
598 tree.root = new_tree(rx->TotalFiles);
599 tree.root->fname = nofname;
603 * For display purposes, the same JobId, with different volumes may
604 * appear more than once, however, we only insert it once.
607 for (p=rx->JobIds; next_jobid_from_list(&p, &JobId) > 0; ) {
609 if (JobId == last_JobId) {
610 continue; /* eliminate duplicate JobIds */
613 bsendmsg(ua, _("Building directory tree for JobId %u ...\n"), JobId);
616 * Find files for this JobId and insert them in the tree
618 Mmsg(&rx->query, uar_sel_files, JobId);
619 if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
620 bsendmsg(ua, "%s", db_strerror(ua->db));
623 * Find the FileSets for this JobId and add to the name_list
625 Mmsg(&rx->query, uar_mediatype, JobId);
626 if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
627 bsendmsg(ua, "%s", db_strerror(ua->db));
630 bsendmsg(ua, "%d Job%s inserted into the tree and marked for extraction.\n",
631 items, items==1?"":"s");
633 /* Check MediaType and select storage that corresponds */
634 get_storage_from_mediatype(ua, &rx->name_list, rx);
636 if (find_arg(ua, _("all")) < 0) {
637 /* Let the user select which files to restore */
638 user_select_files_from_tree(&tree);
642 * Walk down through the tree finding all files marked to be
643 * extracted making a bootstrap file.
645 for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
646 Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
648 Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
649 add_findex(rx->bsr, node->JobId, node->FileIndex);
650 rx->selected_files++;
654 free_tree(tree.root); /* free the directory tree */
659 * This routine is used to get the current backup or a backup
660 * before the specified date.
662 static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
667 char fileset_name[MAX_NAME_LENGTH];
671 /* Create temp tables */
672 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
673 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
674 if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) {
675 bsendmsg(ua, "%s\n", db_strerror(ua->db));
677 if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) {
678 bsendmsg(ua, "%s\n", db_strerror(ua->db));
681 * Select Client from the Catalog
683 memset(&cr, 0, sizeof(cr));
684 if (!get_client_dbr(ua, &cr)) {
687 bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
692 Mmsg(&rx->query, uar_sel_fileset, cr.ClientId, cr.ClientId);
693 start_prompt(ua, _("The defined FileSet resources are:\n"));
694 if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
695 bsendmsg(ua, "%s\n", db_strerror(ua->db));
697 if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
698 fileset_name, sizeof(fileset_name)) < 0) {
701 fsr.FileSetId = atoi(fileset_name); /* Id is first part of name */
702 if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
703 bsendmsg(ua, _("Error getting FileSet record: %s\n"), db_strerror(ua->db));
704 bsendmsg(ua, _("This probably means you modified the FileSet.\n"
705 "Continuing anyway.\n"));
709 /* Find JobId of last Full backup for this client, fileset */
710 Mmsg(&rx->query, uar_last_full, cr.ClientId, cr.ClientId, date, fsr.FileSetId);
711 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
712 bsendmsg(ua, "%s\n", db_strerror(ua->db));
716 /* Find all Volumes used by that JobId */
717 if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
718 bsendmsg(ua, "%s\n", db_strerror(ua->db));
721 /* Note, this is needed as I don't seem to get the callback
722 * from the call just above.
725 if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
726 bsendmsg(ua, "%s\n", db_strerror(ua->db));
728 if (rx->JobTDate == 0) {
729 bsendmsg(ua, _("No Full backup before %s found.\n"), date);
733 /* Now find all Incremental/Decremental Jobs after Full save */
734 Mmsg(&rx->query, uar_inc_dec, edit_uint64(rx->JobTDate, ed1), date,
735 cr.ClientId, fsr.FileSetId);
736 if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
737 bsendmsg(ua, "%s\n", db_strerror(ua->db));
740 /* Get the JobIds from that list */
742 rx->last_jobid[0] = 0;
743 if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
744 bsendmsg(ua, "%s\n", db_strerror(ua->db));
747 if (rx->JobIds[0] != 0) {
748 /* Display a list of Jobs selected for this restore */
749 db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1, HORZ_LIST);
751 bsendmsg(ua, _("No jobs found.\n"));
757 db_sql_query(ua->db, uar_del_temp, NULL, NULL);
758 db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
762 /* Return next JobId from comma separated list */
763 static int next_jobid_from_list(char **p, uint32_t *JobId)
769 for (int i=0; i<(int)sizeof(jobid); i++) {
770 if (*q == ',' || *q == 0) {
777 if (jobid[0] == 0 || !is_a_number(jobid)) {
781 *JobId = strtoul(jobid, NULL, 10);
786 * Callback handler to get JobId and FileIndex for files
788 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
790 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
791 rx->JobId = atoi(row[0]);
792 add_findex(rx->bsr, rx->JobId, atoi(row[1]));
798 * Callback handler make list of JobIds
800 static int jobid_handler(void *ctx, int num_fields, char **row)
802 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
804 if (strcmp(rx->last_jobid, row[0]) == 0) {
805 return 0; /* duplicate id */
807 bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
808 if (rx->JobIds[0] != 0) {
809 pm_strcat(&rx->JobIds, ",");
811 pm_strcat(&rx->JobIds, row[0]);
817 * Callback handler to pickup last Full backup JobTDate
819 static int last_full_handler(void *ctx, int num_fields, char **row)
821 RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
823 rx->JobTDate = strtoll(row[1], NULL, 10);
829 * Callback handler build fileset prompt list
831 static int fileset_handler(void *ctx, int num_fields, char **row)
833 char prompt[MAX_NAME_LENGTH+200];
835 snprintf(prompt, sizeof(prompt), "%s %s %s", row[0], row[1], row[2]);
836 add_prompt((UAContext *)ctx, prompt);
841 * Called here with each name to be added to the list. The name is
842 * added to the list if it is not already in the list.
844 * Used to make unique list of FileSets and MediaTypes
846 static int unique_name_list_handler(void *ctx, int num_fields, char **row)
848 NAME_LIST *name = (NAME_LIST *)ctx;
850 if (name->num_ids == MAX_ID_LIST_LEN) {
853 if (name->num_ids == name->max_ids) {
854 if (name->max_ids == 0) {
855 name->max_ids = 1000;
856 name->name = (char **)bmalloc(sizeof(char *) * name->max_ids);
858 name->max_ids = (name->max_ids * 3) / 2;
859 name->name = (char **)brealloc(name->name, sizeof(char *) * name->max_ids);
862 for (int i=0; i<name->num_ids; i++) {
863 if (strcmp(name->name[i], row[0]) == 0) {
864 return 0; /* already in list, return */
867 /* Add new name to list */
868 name->name[name->num_ids++] = bstrdup(row[0]);
874 * Print names in the list
876 static void print_name_list(UAContext *ua, NAME_LIST *name_list)
878 for (int i=0; i < name_list->num_ids; i++) {
879 bsendmsg(ua, "%s\n", name_list->name[i]);
885 * Free names in the list
887 static void free_name_list(NAME_LIST *name_list)
889 for (int i=0; i < name_list->num_ids; i++) {
890 free(name_list->name[i]);
892 if (name_list->name) {
893 free(name_list->name);
895 name_list->max_ids = 0;
896 name_list->num_ids = 0;
899 static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx)
901 char name[MAX_NAME_LENGTH];
904 if (name_list->num_ids > 1) {
905 bsendmsg(ua, _("Warning, the JobIds that you selected refer to more than one MediaType.\n"
906 "Restore is not possible. The MediaTypes used are:\n"));
907 print_name_list(ua, name_list);
908 rx->store = select_storage_resource(ua);
912 if (name_list->num_ids == 0) {
913 bsendmsg(ua, _("No MediaType found for your JobIds.\n"));
914 rx->store = select_storage_resource(ua);
918 start_prompt(ua, _("The defined Storage resources are:\n"));
920 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
921 if (strcmp(store->media_type, name_list->name[0]) == 0) {
922 add_prompt(ua, store->hdr.name);
926 do_prompt(ua, _("Storage"), _("Select Storage resource"), name, sizeof(name));
927 rx->store = (STORE *)GetResWithName(R_STORAGE, name);
929 bsendmsg(ua, _("\nWarning. Unable to find Storage resource for\n"
930 "MediaType %s, needed by the Jobs you selected.\n"
931 "You will be allowed to select a Storage device later.\n"),