From: Eric Bollengier Date: Thu, 16 Mar 2023 08:52:49 +0000 (+0100) Subject: Fix #9968 Enhance restricted Console support X-Git-Tag: Release-13.0.3~35 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6bfd58a6246c42745c5d5595e713f5bc502f9549;p=thirdparty%2Fbacula.git Fix #9968 Enhance restricted Console support - Limit job list in restore menu 1 and 2 - Adapt restore menu 3 jobid selection - Adapt restore menu 11 jobid selection - Adapt restore jobid= parameter - Add checks on restore file parameter - Adapt purge commands --- diff --git a/bacula/src/cats/bdb.h b/bacula/src/cats/bdb.h index 52e277cb1..03f0cfd13 100644 --- a/bacula/src/cats/bdb.h +++ b/bacula/src/cats/bdb.h @@ -214,6 +214,7 @@ public: bool bdb_create_batch_file_attributes_record(JCR *jcr, ATTR_DBR *ar); /* sql_get.c */ + char *bdb_get_jobids(const char *jobids, POOLMEM **ret, bool append); bool bdb_get_file_record(JCR *jcr, JOB_DBR *jr, FILE_DBR *fdbr); bool bdb_get_snapshot_record(JCR *jcr, SNAPSHOT_DBR *snap); bool bdb_get_volume_jobids(JCR *jcr, @@ -256,6 +257,7 @@ public: /* sql_list.c */ void bdb_list_pool_records(JCR *jcr, POOL_DBR *pr, DB_LIST_HANDLER sendit, void *ctx, e_list_type type); alist *bdb_list_job_records(JCR *jcr, JOB_DBR *jr, DB_LIST_HANDLER sendit, void *ctx, e_list_type type); + void bdb_list_jobs_for_file(JCR *jcr, const char *client, const char *fname, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type); void bdb_list_job_totals(JCR *jcr, JOB_DBR *jr, DB_LIST_HANDLER sendit, void *ctx); void bdb_list_files_for_job(JCR *jcr, uint32_t jobid, int deleted, DB_LIST_HANDLER sendit, void *ctx); void bdb_list_media_records(JCR *jcr, MEDIA_DBR *mdbr, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type); diff --git a/bacula/src/cats/cats.h b/bacula/src/cats/cats.h index c1197188f..b124208cd 100644 --- a/bacula/src/cats/cats.h +++ b/bacula/src/cats/cats.h @@ -676,7 +676,8 @@ enum e_list_type { ARG_LIST, /* key1=v1 key2=v2 key3=v3 */ JSON_LIST, FAILED_JOBS, - INCOMPLETE_JOBS + INCOMPLETE_JOBS, + LAST_JOBS }; #include "bdb.h" diff --git a/bacula/src/cats/protos.h b/bacula/src/cats/protos.h index 53e668251..28ff00cc1 100644 --- a/bacula/src/cats/protos.h +++ b/bacula/src/cats/protos.h @@ -81,6 +81,7 @@ BDB *db_init_database(JCR *jcr, const char *db_driver, const char *db_name, if (mdb) mdb->bdb_thread_cleanup() /* sql.c */ +int db_jobids_handler(void *ctx, int num_fields, char **row); int db_mint64_handler(void *ctx, int num_fields, char **row); int db_int64_handler(void *ctx, int num_fields, char **row); int db_strtime_handler(void *ctx, int num_fields, char **row); @@ -109,6 +110,9 @@ void bdb_free_restoreobject_record(JCR *jcr, ROBJECT_DBR *rr); #define db_get_client_pool(jcr, mdb, results) \ mdb->bdb_get_client_pool(jcr, results) +#define db_get_jobids(jcr, mdb, jobids, ret, append) \ + mdb->bdb_get_jobids(jobids, ret, append) + /* sql_create.c */ #define db_create_log_record(jcr, mdb, jobid, mtime, msg) \ mdb->bdb_create_log_record(jcr, jobid, mtime, msg) @@ -263,6 +267,8 @@ void bdb_free_restoreobject_record(JCR *jcr, ROBJECT_DBR *rr); #define db_get_job_statistics(jcr, mdb, jr) \ mdb->bdb_get_job_statistics(jcr, jr) /* sql_list.c */ +#define db_list_jobs_for_file(jcr, mdb, cli, fname, result_handler, ctx, type) \ + mdb->bdb_list_jobs_for_file(jcr, cli, fname, result_handler, ctx, type) #define db_list_pool_records(jcr, mdb, pr, sendit, ctx, type) \ mdb->bdb_list_pool_records(jcr, pr, sendit, ctx, type) #define db_list_job_records(jcr, mdb, jr, sendit, ctx, type) \ diff --git a/bacula/src/cats/sql.c b/bacula/src/cats/sql.c index 85bda0ae8..558e35d6d 100644 --- a/bacula/src/cats/sql.c +++ b/bacula/src/cats/sql.c @@ -57,9 +57,9 @@ dbid_list::~dbid_list() free(DBId); } -/* - * Called here to retrieve an resource name (e.g. Storage name) from the database - */ +/* + * Called here to retrieve a resource name (e.g. Storage name) from the database + */ int db_name_handler(void *ctx, int num_fields, char **row) { char *name = (char *)ctx; @@ -74,6 +74,21 @@ int db_name_handler(void *ctx, int num_fields, char **row) return 0; } +/* + * Called here to retrieve a list of jobid x,y,z from the database + */ +int db_jobids_handler(void *ctx, int num_fields, char **row) +{ + POOLMEM **ret = (POOLMEM **)ctx; + if (row[0]) { + if (**ret) { + pm_strcat(ret, ","); + } + pm_strcat(ret, row[0]); + } + return 0; +} + /* * Called here to retrieve an string list from the database */ @@ -226,7 +241,6 @@ BDB::~BDB() */ char *BDB::get_acls(int tables, bool where /* use WHERE or AND */) { - POOL_MEM tmp; pm_strcpy(acl_where, ""); for (int i=0 ; i < DB_ACL_LAST; i++) { @@ -238,6 +252,58 @@ char *BDB::get_acls(int tables, bool where /* use WHERE or AND */) return acl_where; } +char *BDB::bdb_get_jobids(const char *jobids, POOLMEM **ret, bool append) +{ + if (!ret || !*ret) { + return NULL; + } + + if (!append) { // Return empty list if append = false + pm_strcpy(ret, ""); + } + + if (!jobids || !*jobids || !is_a_number_list(jobids)) { + return *ret; + } + + bdb_lock(); + /* Get optional filters for the SQL query */ + const char *where = get_acls(DB_ACL_BIT(DB_ACL_JOB) | + DB_ACL_BIT(DB_ACL_CLIENT) | + DB_ACL_BIT(DB_ACL_FILESET), false); + + const char *join = *where ? get_acl_join_filter(DB_ACL_BIT(DB_ACL_CLIENT) | + DB_ACL_BIT(DB_ACL_FILESET)) : ""; + /* No filters, no need to run the query */ + if (!*where && !*join) { + if (*ret[0]) { + pm_strcat(ret, ","); + } + pm_strcat(ret, jobids); + goto bail_out; + } + Mmsg(cmd, "SELECT Job.JobId as JobId " + "FROM Job %s WHERE JobId IN (%s%s%s) %s ORDER BY JobTDate ASC", + join, + *ret /* previous list */, + *ret[0] ? "," : "" /* comma to separate from the new list */, + jobids, + where); + + /* We start from a fresh list, previous entries will be listed */ + pm_strcpy(ret, ""); + + Dmsg1(50|DT_SQL, "q=%s\n", cmd); + if(!bdb_sql_query(cmd, db_jobids_handler, ret)) { + goto bail_out; + } + +bail_out: + sql_free_result(); + bdb_unlock(); + return *ret; +} + /* Create the JOIN string that will help to filter queries results */ char *BDB::get_acl_join_filter(int tables) { diff --git a/bacula/src/cats/sql_cmds.c b/bacula/src/cats/sql_cmds.c index ae799dc6f..af0e203eb 100644 --- a/bacula/src/cats/sql_cmds.c +++ b/bacula/src/cats/sql_cmds.c @@ -101,13 +101,6 @@ const char *create_delindex = "CREATE INDEX DelInx1 ON DelCandidates (JobId)"; const char *uar_count_files = "SELECT JobFiles FROM Job WHERE JobId=%s"; -/* List last 20 Jobs */ -const char *uar_list_jobs = - "SELECT JobId,Client.Name as Client,Job.Name as Name,StartTime,Level as " - "JobLevel,JobFiles,JobBytes " - "FROM Client,Job WHERE Client.ClientId=Job.ClientId AND JobStatus IN ('T','W') " - "AND Type='B' ORDER BY StartTime DESC LIMIT 20"; - const char *uar_print_jobs = "SELECT DISTINCT JobId,Level,JobFiles,JobBytes,StartTime,VolumeName" " FROM Job JOIN JobMedia USING (JobId) JOIN Media USING (MediaId) " @@ -248,9 +241,11 @@ const char *uar_jobids_fileindex = "AND Path.PathId=File.PathId " "ORDER BY Job.StartTime DESC LIMIT 1"; -/* Query to get list of files from table -- presuably built by an external program */ +/* Query to get list of files from table -- presuably built by an external program + * we expect filters on join and where + */ const char *uar_jobid_fileindex_from_table = - "SELECT JobId, FileIndex FROM %s ORDER BY JobId, FileIndex ASC"; + "SELECT JobId, FileIndex FROM %s %s %s ORDER BY JobId, FileIndex ASC"; /* Get the list of the last recent version per Delta with a given * jobid list. This is a tricky part because with SQL the result of: @@ -553,40 +548,6 @@ const char *uap_upgrade_copies_oldest_job[] = /* ======= ua_restore.c ====== */ -/* List Jobs where a particular file is saved */ -const char *uar_file[] = -{ - /* MySQL */ - "SELECT Job.JobId as JobId," - "CONCAT(Path.Path,File.Filename) as Name, " - "StartTime,Type as JobType,JobStatus,JobFiles,JobBytes " - "FROM Client,Job,File,Path WHERE Client.Name='%s' " - "AND Client.ClientId=Job.ClientId " - "AND Job.JobId=File.JobId AND File.FileIndex > 0 " - "AND Path.PathId=File.PathId " - "AND File.Filename='%s' ORDER BY StartTime DESC LIMIT 20", - - /* PostgreSQL */ - "SELECT Job.JobId as JobId," - "Path.Path||File.Filename as Name, " - "StartTime,Type as JobType,JobStatus,JobFiles,JobBytes " - "FROM Client,Job,File,Path WHERE Client.Name='%s' " - "AND Client.ClientId=Job.ClientId " - "AND Job.JobId=File.JobId AND File.FileIndex > 0 " - "AND Path.PathId=File.PathId " - "AND File.Filename='%s' ORDER BY StartTime DESC LIMIT 20", - - /* SQLite */ - "SELECT Job.JobId as JobId," - "Path.Path||File.Filename as Name, " - "StartTime,Type as JobType,JobStatus,JobFiles,JobBytes " - "FROM Client,Job,File,Path WHERE Client.Name='%s' " - "AND Client.ClientId=Job.ClientId " - "AND Job.JobId=File.JobId AND File.FileIndex > 0 " - "AND Path.PathId=File.PathId " - "AND File.Filename='%s' ORDER BY StartTime DESC LIMIT 20" -}; - const char *uar_create_temp[] = { /* MySQL */ diff --git a/bacula/src/cats/sql_list.c b/bacula/src/cats/sql_list.c index 06b8f8d8f..ef16acb3c 100644 --- a/bacula/src/cats/sql_list.c +++ b/bacula/src/cats/sql_list.c @@ -797,6 +797,13 @@ alist *BDB::bdb_list_job_records(JCR *jcr, JOB_DBR *jr, DB_LIST_HANDLER *sendit, "FROM Job %s %s ORDER BY StartTime %s,JobId %s %s", join, where, order, order, limit); break; + case LAST_JOBS: + Mmsg(cmd, + "SELECT JobId,Client1.Name as Client,Job.Name as Name,StartTime,Level as " + "JobLevel,JobFiles,JobBytes " + "FROM Client AS Client1 JOIN Job USING (ClientId) %s %s AND JobStatus IN ('T','W') " + "ORDER BY StartTime %s %s", join, where, order, limit); + default: break; } @@ -1135,4 +1142,55 @@ void BDB::bdb_list_tag_records(JCR *jcr, TAG_DBR *tag, DB_LIST_HANDLER *result_h bdb_unlock(); } +/* List all file records from a job + * "deleted" values are described just below + */ +void BDB::bdb_list_jobs_for_file(JCR *jcr, const char *client, const char *fname, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type) +{ + if (!client || !*client || !fname || !*fname) { + return; + } + const char *concat="Path.Path||File.Filename"; + + if (bdb_get_type_index() == SQL_TYPE_MYSQL) { + concat = " CONCAT(Path.Path,File.Filename) "; + } + + bdb_lock(); + /* Get optional filters for the SQL query */ + const char *where = get_acls(DB_ACL_BIT(DB_ACL_JOB) | + DB_ACL_BIT(DB_ACL_CLIENT) | + DB_ACL_BIT(DB_ACL_FILESET), false); + + const char *join = *where ? get_acl_join_filter(DB_ACL_BIT(DB_ACL_FILESET)) : ""; + + int len = strlen(fname); + char *esc = (char *)malloc(len * 2 + 1); + bdb_escape_string(jcr, esc, (char *)fname, len); + + len = strlen(client); + char *esc2 = (char *)malloc(len * 2 + 1); + bdb_escape_string(jcr, esc2, (char *)client, len); + + Mmsg(cmd, "SELECT Job.JobId as JobId," + "%s as Name, " // Concat of the filename + "StartTime, Type as JobType, JobStatus,JobFiles,JobBytes " + "FROM Client JOIN Job USING (ClientId) JOIN File USING (JobId) JOIN Path USING (PathId) %s " + "WHERE Client.Name = '%s' " + "AND File.FileIndex > 0 " + "AND File.Filename='%s' %s ORDER BY StartTime DESC LIMIT 20", concat, join, esc2, esc, where); + + free(esc); + free(esc2); + Dmsg1(DT_SQL|50, "q=%s\n", cmd); + if (!QueryDB(jcr, cmd)) { + goto bail_out; + } + + list_result(jcr, this, "job", sendit, ctx, HORZ_LIST); +bail_out: + sql_free_result(); + bdb_unlock(); +} + #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */ diff --git a/bacula/src/dird/autoprune.c b/bacula/src/dird/autoprune.c index a5fc1c99a..3ce6b7269 100644 --- a/bacula/src/dird/autoprune.c +++ b/bacula/src/dird/autoprune.c @@ -181,7 +181,7 @@ void prune_volumes(JCR *jcr, bool InChanger, MEDIA_DBR *mr, count = get_prune_list_for_volume(ua, &lmr, &prune_list); Dmsg1(100, "Num pruned = %d\n", count); if (count != 0) { - purge_job_list_from_catalog(ua, prune_list); + purge_job_list_from_catalog(ua, prune_list); /* JobId list not coming from outside */ prune_list.num_ids = 0; /* reset count */ } if (!is_volume_purged(ua, &lmr)) { diff --git a/bacula/src/dird/mac.c b/bacula/src/dird/mac.c index bb63c1603..52c79bb01 100644 --- a/bacula/src/dird/mac.c +++ b/bacula/src/dird/mac.c @@ -775,10 +775,10 @@ void mac_cleanup(JCR *jcr, int TermCode, int writeTermCode) if (jcr->job->PurgeMigrateJob) { /* Purge old Job record */ - purge_jobs_from_catalog(ua, old_jobid); + purge_jobs_from_catalog(ua, old_jobid); // JobId is safe } else { /* Purge all old file records, but leave Job record */ - purge_files_from_jobs(ua, old_jobid); + purge_files_from_jobs(ua, old_jobid); // JobId is safe } free_ua_context(ua); diff --git a/bacula/src/dird/ua_purge.c b/bacula/src/dird/ua_purge.c index 64df5f7e0..a8c23b7c9 100644 --- a/bacula/src/dird/ua_purge.c +++ b/bacula/src/dird/ua_purge.c @@ -37,13 +37,13 @@ static int purge_jobs_from_client(UAContext *ua, CLIENT *client, char *job); int truncate_cmd(UAContext *ua, const char *cmd); static const char *select_jobsfiles_from_client = - "SELECT JobId FROM Job " - "WHERE ClientId=%s " + "SELECT JobId FROM Job %s " + "WHERE ClientId=%s %s " "AND PurgedFiles=0"; static const char *select_jobs_from_client = - "SELECT JobId, PurgedFiles FROM Job " - "WHERE ClientId=%s"; + "SELECT JobId, PurgedFiles FROM Job %s " + "WHERE ClientId=%s %s"; /* * Purge records from database @@ -105,9 +105,11 @@ int purge_cmd(UAContext *ua, const char *cmd) case 0: /* Job */ case 1: /* JobId */ if (get_job_dbr(ua, &jr)) { - char jobid[50]; - edit_int64(jr.JobId, jobid); - purge_files_from_jobs(ua, jobid); + if (acl_access_ok(ua, Job_ACL, jr.Name)) { + char jobid[50]; + edit_int64(jr.JobId, jobid); + purge_files_from_jobs(ua, jobid); + } } return 1; case 2: /* client */ @@ -233,9 +235,22 @@ static int purge_files_from_client(UAContext *ua, CLIENT *client) ua->send_events("DC0001", EVENTS_TYPE_COMMAND, "purge files client=%s", cr.Name); ua->info_msg(_("Begin purging files for Client \"%s\"\n"), cr.Name); - Mmsg(query, select_jobsfiles_from_client, edit_int64(cr.ClientId, ed1)); - Dmsg1(050, "select sql=%s\n", query.c_str()); - db_sql_query(ua->db, query.c_str(), file_delete_handler, (void *)&del); + db_lock(ua->db); + { + /* Get optional filters for the SQL query */ + const char *where = ua->db->get_acls(DB_ACL_BIT(DB_ACL_JOB) | + DB_ACL_BIT(DB_ACL_CLIENT) | + DB_ACL_BIT(DB_ACL_FILESET), false); + + const char *join = *where ? ua->db->get_acl_join_filter(DB_ACL_BIT(DB_ACL_JOB) | + DB_ACL_BIT(DB_ACL_CLIENT) | + DB_ACL_BIT(DB_ACL_FILESET)) : ""; + + Mmsg(query, select_jobsfiles_from_client, join, edit_int64(cr.ClientId, ed1), where); + Dmsg1(050, "select sql=%s\n", query.c_str()); + db_sql_query(ua->db, query.c_str(), file_delete_handler, (void *)&del); + } + db_unlock(ua->db); purge_files_from_job_list(ua, del); @@ -283,24 +298,40 @@ static int purge_jobs_from_client(UAContext *ua, CLIENT *client, char* job) del.JobId = (JobId_t *)malloc(sizeof(JobId_t) * del.max_ids); del.PurgedFiles = (char *)malloc(del.max_ids); + /* We filter the JobId list with ACL console if any */ + db_lock(ua->db); + { + /* Get optional filters for the SQL query */ + const char *where = ua->db->get_acls(DB_ACL_BIT(DB_ACL_JOB) | + DB_ACL_BIT(DB_ACL_CLIENT) | + DB_ACL_BIT(DB_ACL_FILESET), false); + + const char *join = *where ? ua->db->get_acl_join_filter(DB_ACL_BIT(DB_ACL_CLIENT) | + DB_ACL_BIT(DB_ACL_FILESET)) : ""; + + if (job) { + /* Limit purged jobs to specified job's name */ + char esc[MAX_ESCAPE_NAME_LENGTH]; + db_escape_string(ua->jcr, ua->jcr->db, esc, job, strlen(job)); + Mmsg(query, "SELECT JobId,PurgedFiles FROM Job %s WHERE ClientId=%s AND Name='%s' %s", + join, edit_int64(cr.ClientId, ed1), esc, where); + + } else { + Mmsg(query, select_jobs_from_client, join, edit_int64(cr.ClientId, ed1), where); + } + } + db_unlock(ua->db); if (job) { - /* Limit purged jobs to specified job's name */ - char esc[MAX_ESCAPE_NAME_LENGTH]; - db_escape_string(ua->jcr, ua->jcr->db, esc, job, strlen(job)); - Mmsg(query, "SELECT JobId,PurgedFiles FROM Job WHERE ClientId=%s AND Name='%s'", - edit_int64(cr.ClientId, ed1), esc); ua->info_msg(_("Begin purging jobs \"%s\" from Client \"%s\"\n"), job, cr.Name); - } else { - Mmsg(query, select_jobs_from_client, edit_int64(cr.ClientId, ed1)); ua->info_msg(_("Begin purging jobs from Client \"%s\"\n"), cr.Name); } - + Dmsg1(150, "select sql=%s\n", query.c_str()); db_sql_query(ua->db, query.c_str(), job_delete_handler, (void *)&del); - purge_job_list_from_catalog(ua, del); + purge_job_list_from_catalog(ua, del); /* JobId list secured */ if (del.num_del == 0) { ua->warning_msg(_("No Jobs found for client %s to purge from %s catalog.\n"), @@ -319,9 +350,8 @@ static int purge_jobs_from_client(UAContext *ua, CLIENT *client, char* job) return 1; } - /* - * Remove File records from a list of JobIds + * Remove File records from a list of JobIds. Jobs must be secured. */ void purge_files_from_jobs(UAContext *ua, char *jobs) { @@ -361,6 +391,7 @@ void purge_files_from_jobs(UAContext *ua, char *jobs) /* * Delete jobs (all records) from the catalog in groups of 1000 * at a time. + * The JobId list must be secured when entering here */ void purge_job_list_from_catalog(UAContext *ua, del_ctx &del) { @@ -471,6 +502,8 @@ void upgrade_copies(UAContext *ua, char *jobs) /* * Remove all records from catalog for a list of JobIds + * The code in tools/dbcheck.c has to be updated as well + * jobs must be secured. */ void purge_jobs_from_catalog(UAContext *ua, char *jobs) { @@ -571,13 +604,24 @@ bool purge_jobs_from_volume(UAContext *ua, MEDIA_DBR *mr, bool force) jobids = lst.list; } + /* We filter out the list of JobId using the one we are allowed by ACL */ + jobids = db_get_jobids(ua->jcr, ua->db, jobids, query.handle(), false); + + /* Get the right count of job (count the number of , + 1) */ + i = (*jobids == 0) ? 1 : 0; + for (char *p = jobids; *p ; p++) { + if (*p == ',') { + i++; + } + } + if (*jobids) { /* Keep track of this important event */ ua->send_events("DC0003", EVENTS_TYPE_COMMAND, "purge volume=%s", mr->VolumeName); - purge_jobs_from_catalog(ua, jobids); + purge_jobs_from_catalog(ua, jobids); /* JobId list secured */ ua->info_msg(_("%d Job%s on Volume \"%s\" purged from catalog.\n"), - lst.count, lst.count<=1?"":"s", mr->VolumeName); + i, i<=1?"":"s", mr->VolumeName); } purged = is_volume_purged(ua, mr, force); diff --git a/bacula/src/dird/ua_restore.c b/bacula/src/dird/ua_restore.c index d46fb5ddc..926264184 100644 --- a/bacula/src/dird/ua_restore.c +++ b/bacula/src/dird/ua_restore.c @@ -730,10 +730,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) if (!has_value(ua, i)) { return 0; } - if (*rx->JobIds != 0) { - pm_strcat(rx->JobIds, ","); - } - pm_strcat(rx->JobIds, ua->argv[i]); + db_get_jobids(ua->jcr, ua->db, ua->argv[i], &rx->JobIds, true /* append the result */); done = true; break; case 1: /* current */ @@ -825,7 +822,6 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) /* If choice not already made above, prompt */ for ( ; !done; ) { - char *fname; int len; bool gui_save; db_list_ctx jobids; @@ -843,11 +839,18 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) ua->error_msg(_("SQL query not authorized.\n")); return 0; } - gui_save = ua->jcr->gui; - ua->jcr->gui = true; - db_list_sql_query(ua->jcr, ua->db, "job", uar_list_jobs, prtit, ua, 1, HORZ_LIST); - ua->jcr->gui = gui_save; - done = false; + { + memset(&jr, 0, sizeof(jr)); + jr.limit=20; + jr.JobType = 'B'; + jr.order = 1; // DESC + gui_save = ua->jcr->gui; + ua->jcr->gui = true; + /* db_list_job_records() has builtin console filters */ + db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, LAST_JOBS); + ua->jcr->gui = gui_save; + done = false; + } break; case 1: /* list where a file is saved */ if (!get_client_name(ua, rx)) { @@ -856,14 +859,9 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) if (!get_cmd(ua, _("Enter Filename (no path):"))) { return 0; } - len = strlen(ua->cmd); - fname = (char *)malloc(len * 2 + 1); - db_escape_string(ua->jcr, ua->db, fname, ua->cmd, len); - Mmsg(rx->query, uar_file[db_get_type_index(ua->db)], rx->ClientName, fname); - free(fname); gui_save = ua->jcr->gui; ua->jcr->gui = true; - db_list_sql_query(ua->jcr, ua->db, "job", rx->query, prtit, ua, 1, HORZ_LIST); + db_list_jobs_for_file(ua->jcr, ua->db, rx->ClientName, ua->cmd, prtit, ua, HORZ_LIST); ua->jcr->gui = gui_save; done = false; break; @@ -871,7 +869,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) { return 0; } - pm_strcpy(rx->JobIds, ua->cmd); + db_get_jobids(ua->jcr, ua->db, ua->cmd, &rx->JobIds, false /* clear */); break; case 3: /* Enter an SQL list command */ if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) { @@ -923,6 +921,9 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) if (len == 0) { break; } + if (ua->cmd[0] == '?') { + continue; // We do not want to accept a SQL table here + } insert_one_file_or_dir(ua, rx, date, false); } return 2; @@ -946,6 +947,9 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) if (len == 0) { break; } + if (ua->cmd[0] == '?') { + continue; // We do not want to accept a SQL table here + } insert_one_file_or_dir(ua, rx, date, false); } return 2; @@ -976,15 +980,19 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) if (*rx->JobIds != 0) { ua->send_msg(_("You have already selected the following JobIds: %s\n"), rx->JobIds); + } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) { - if (*rx->JobIds != 0 && *ua->cmd) { - pm_strcat(rx->JobIds, ","); + if (*ua->cmd == '.') { + *rx->JobIds = 0; + return 0; /* nothing entered, return */ } - pm_strcat(rx->JobIds, ua->cmd); - } - if (*rx->JobIds == 0 || *rx->JobIds == '.') { - *rx->JobIds = 0; - return 0; /* nothing entered, return */ + db_get_jobids(ua->jcr, ua->db, ua->cmd, &rx->JobIds, false /* clear */); + if (*rx->JobIds == 0) { + ua->error_msg(_("No JobId selected\n")); + return 0; /* nothing entered, return */ + } + ua->send_msg(_("You have selected the following JobIds: %s\n"), + rx->JobIds); } if (!have_date) { bstrutime(date, sizeof(date), now); @@ -1007,6 +1015,9 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) if (ua->cmd[0] != '<' && !IsPathSeparator(ua->cmd[len-1])) { strcat(ua->cmd, "/"); } + if (ua->cmd[0] == '?') { + continue; // We do not want to accept a SQL table here + } insert_one_file_or_dir(ua, rx, date, true); } return 2; @@ -1113,7 +1124,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) } //TODO Validation if id entered matches one from list above would be nice... - if (!get_pint(ua, "Enter ID of Object to be restored: ")) { + if (!get_pint(ua, _("Enter ID of Object to be restored: "))) { ua->info_msg(_("Selection aborted, nothing done.\n")); break; } @@ -1350,14 +1361,32 @@ bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table) { component_file_ctx ctx; strip_trailing_junk(table); - Mmsg(rx->query, uar_jobid_fileindex_from_table, table); + if (strcasecmp(table, "file") == 0 || !is_name_valid(table, NULL, "")) { + ua->error_msg(_("Incorrect table name\n")); + return false; + } + bool ret; + db_lock(ua->db); + { + /* Get optional filters for the SQL query */ + const char *where = ua->db->get_acls(DB_ACL_BIT(DB_ACL_JOB) | + DB_ACL_BIT(DB_ACL_CLIENT) | + DB_ACL_BIT(DB_ACL_FILESET), true); - rx->found = false; + const char *join = *where ? ua->db->get_acl_join_filter(DB_ACL_BIT(DB_ACL_JOB) | + DB_ACL_BIT(DB_ACL_CLIENT) | + DB_ACL_BIT(DB_ACL_FILESET)) : ""; - /* Find and insert jobid and File Index. The JobIds are stored in rx->JobIds */ - if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) { + Mmsg(rx->query, uar_jobid_fileindex_from_table, table, join, where); + rx->found = false; + + /* Find and insert jobid and File Index. The JobIds are stored in rx->JobIds */ + ret = db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx); + } + db_unlock(ua->db); + if (!ret) { ua->error_msg(_("Query failed: %s. ERR=%s\n"), - rx->query, db_strerror(ua->db)); + rx->query, db_strerror(ua->db)); } if (!rx->found) { ua->error_msg(_("No table found: %s\n"), table); diff --git a/bacula/src/stored/bsdjson.c b/bacula/src/stored/bsdjson.c index a61599f98..24a12c664 100644 --- a/bacula/src/stored/bsdjson.c +++ b/bacula/src/stored/bsdjson.c @@ -343,6 +343,7 @@ static void display_transfer_priority(HPKT &hpkt) } } } + /* * Dump out all resources in json format. * Note!!!! This routine must be in this file rather