]> git.sur5r.net Git - bacula/bacula/commitdiff
Fix bug #1355 Director crashes with double free in Accurate SQL query
authorKern Sibbald <kern@sibbald.com>
Sun, 30 Aug 2009 18:29:15 +0000 (20:29 +0200)
committerKern Sibbald <kern@sibbald.com>
Sun, 30 Aug 2009 18:29:15 +0000 (20:29 +0200)
13 files changed:
bacula/src/cats/bvfs.c
bacula/src/cats/bvfs.h
bacula/src/cats/cats.h
bacula/src/cats/protos.h
bacula/src/cats/sql.c
bacula/src/cats/sql_get.c
bacula/src/dird/backup.c
bacula/src/dird/ua_dotcmds.c
bacula/src/dird/ua_output.c
bacula/src/dird/ua_restore.c
bacula/src/dird/vbackup.c
bacula/src/lib/edit.c
bacula/technotes

index c27ab50fceedfe5ace7d25a88e1294c912dceec7..b4feb802097f96f52f6b098900f78a532cc25249 100644 (file)
@@ -59,9 +59,8 @@ Bvfs::Bvfs(JCR *j, B_DB *mdb) {
    jcr->inc_use_count();
    db = mdb;                 /* need to inc ref count */
    prev_dir = get_pool_memory(PM_NAME);
-   jobids = get_pool_memory(PM_NAME);
    pattern = get_pool_memory(PM_NAME);
-   *prev_dir = *pattern = *jobids = 0;
+   *prev_dir = *pattern = 0;
    dir_filenameid = pwd_id = offset = 0;
    see_copies = see_all_version = false;
    limit = 1000;
@@ -71,7 +70,6 @@ Bvfs::Bvfs(JCR *j, B_DB *mdb) {
 }
 
 Bvfs::~Bvfs() {
-   free_pool_memory(jobids);
    free_pool_memory(pattern);
    free_pool_memory(prev_dir);
    free_attr(attr);
@@ -363,6 +361,8 @@ DBId_t Bvfs::get_dir_filenameid()
 void bvfs_update_cache(JCR *jcr, B_DB *mdb)
 {
    uint32_t nb=0;
+   db_list_ctx jobids;
+
    db_lock(mdb);
    db_start_transaction(jcr, mdb);
 
@@ -406,18 +406,15 @@ void bvfs_update_cache(JCR *jcr, B_DB *mdb)
 
    }
 
-   POOLMEM *jobids = get_pool_memory(PM_NAME);
-   *jobids = 0;
-
    Mmsg(mdb->cmd, 
  "SELECT JobId from Job "
   "WHERE JobId NOT IN (SELECT JobId FROM brestore_knownjobid) "
     "AND Type IN ('B') AND JobStatus IN ('T', 'f', 'A') "
   "ORDER BY JobId");
 
-   db_sql_query(mdb, mdb->cmd, db_get_int_handler, jobids);
+   db_sql_query(mdb, mdb->cmd, db_list_handler, &jobids);
 
-   bvfs_update_path_hierarchy_cache(jcr, mdb, jobids);
+   bvfs_update_path_hierarchy_cache(jcr, mdb, &jobids);
 
    db_end_transaction(jcr, mdb);
    db_start_transaction(jcr, mdb);
@@ -438,28 +435,28 @@ void bvfs_update_cache(JCR *jcr, B_DB *mdb)
    Dmsg1(dbglevel, "Affected row(s) = %d\n", nb);
 
    db_end_transaction(jcr, mdb);
-   free_pool_memory(jobids);
 }
 
 /*
  * Update the bvfs cache for given jobids (1,2,3,4)
  */
 void
-bvfs_update_path_hierarchy_cache(JCR *jcr, B_DB *mdb, char *jobids)
+bvfs_update_path_hierarchy_cache(JCR *jcr, B_DB *mdb, db_list_ctx *jobids)
 {
    pathid_cache ppathid_cache;
    JobId_t JobId;
    char *p;
 
-   for (p=jobids; ; ) {
+   for (p=jobids->list; ; ) {
       int stat = get_next_jobid_from_list(&p, &JobId);
+      Dmsg1(dbglevel, "Updating cache for %lld\n", (uint64_t)JobId);
       if (stat < 0) {
          return;
       }
       if (stat == 0) {
          break;
       }
-      Dmsg1(dbglevel, "Updating cache for %lld\n", (uint64_t) JobId);
+      Dmsg1(dbglevel, "Updating cache for %lld\n", (uint64_t)JobId);
       update_path_hierarchy_cache(jcr, mdb, ppathid_cache, JobId);
    }
 }
@@ -469,7 +466,7 @@ bvfs_update_path_hierarchy_cache(JCR *jcr, B_DB *mdb, char *jobids)
  */
 void Bvfs::update_cache()
 {
-   bvfs_update_path_hierarchy_cache(jcr, db, jobids);
+   bvfs_update_path_hierarchy_cache(jcr, db, &jobids);
 }
 
 /* Change the current directory, returns true if the path exists */
@@ -550,7 +547,7 @@ void Bvfs::ls_special_dirs()
 {
    Dmsg1(dbglevel, "ls_special_dirs(%lld)\n", (uint64_t)pwd_id);
    char ed1[50], ed2[50];
-   if (!*jobids) {
+   if (jobids.count == 0) {
       return;
    }
    if (!dir_filenameid) {
@@ -579,7 +576,7 @@ void Bvfs::ls_special_dirs()
        "AND File1.JobId IN (%s)) AS listfile1 "
   "ON (tmp.PathId = listfile1.PathId) "
   "ORDER BY tmp.Path, JobId DESC ",
-        query.c_str(), edit_uint64(dir_filenameid, ed2), jobids);
+        query.c_str(), edit_uint64(dir_filenameid, ed2), jobids.list);
 
    Dmsg1(dbglevel_sql, "q=%s\n", query2.c_str());
    db_sql_query(db, query2.c_str(), path_handler, this);
@@ -590,7 +587,7 @@ bool Bvfs::ls_dirs()
 {
    Dmsg1(dbglevel, "ls_dirs(%lld)\n", (uint64_t)pwd_id);
    char ed1[50], ed2[50];
-   if (!*jobids) {
+   if (jobids.count == 0) {
       return false;
    }
 
@@ -640,10 +637,10 @@ bool Bvfs::ls_dirs()
        "ON (listpath1.PathId = listfile1.PathId) "
     ") AS A ORDER BY 2,3 DESC LIMIT %d OFFSET %d",
         edit_uint64(pwd_id, ed1),
-        jobids,
+        jobids.list,
         filter.c_str(),
         edit_uint64(dir_filenameid, ed2),
-        jobids,
+        jobids.list,
         limit, offset);
 
    Dmsg1(dbglevel_sql, "q=%s\n", query.c_str());
@@ -661,7 +658,7 @@ bool Bvfs::ls_files()
 {
    Dmsg1(dbglevel, "ls_files(%lld)\n", (uint64_t)pwd_id);
    char ed1[50];
-   if (!*jobids) {
+   if (jobids.count == 0) {
       return false;
    }
 
@@ -690,7 +687,7 @@ bool Bvfs::ls_files()
      ") AS listfiles "
 "WHERE File.FileId = listfiles.id",
         edit_uint64(pwd_id, ed1),
-        jobids,
+        jobids.list,
         filter.c_str(),
         limit,
         offset);
index 9abe7c485e7caf1f1ac24193006d8ba5097faf76..72575abaf61c207ce0137b9cc9fec88d2ffcf949 100644 (file)
@@ -71,11 +71,11 @@ public:
    virtual ~Bvfs();
 
    void set_jobid(JobId_t id) {
-      Mmsg(jobids, "%lld", (uint64_t)id);
+      Mmsg(jobids.list, "%lld", (uint64_t)id);
    }
 
    void set_jobids(char *ids) {
-      pm_strcpy(jobids, ids);
+      pm_strcpy(jobids.list, ids);
    }
 
    void set_limit(uint32_t max) {
@@ -154,7 +154,7 @@ public:
 private:   
    JCR *jcr;
    B_DB *db;
-   POOLMEM *jobids;
+   db_list_ctx jobids;
    uint32_t limit;
    uint32_t offset;
    uint32_t nb_record;          /* number of records of the last query */
@@ -173,7 +173,7 @@ private:
    void *user_data;
 };
 
-void bvfs_update_path_hierarchy_cache(JCR *jcr, B_DB *mdb, char *jobids);
+void bvfs_update_path_hierarchy_cache(JCR *jcr, B_DB *mdb, db_list_ctx *jobids);
 void bvfs_update_cache(JCR *jcr, B_DB *mdb);
 char *bvfs_parent_dir(char *path);
 
index a2913dddffcfe8915542320eada457efc1345f45..4613b67b5c0f05e8367928052b8e02b17dad6d4e 100644 (file)
@@ -1044,6 +1044,16 @@ struct db_int64_ctx {
    int count;                         /* number of values seen */
 };
 
+/* Call back context for getting a list of comma separated strings from the database */
+class db_list_ctx {
+public:
+   POOLMEM *list;                     /* list */
+   int count;                         /* number of values seen */
+
+   db_list_ctx() { list = get_pool_memory(PM_FNAME); *list = 0; count = 0; }
+   ~db_list_ctx() { free_pool_memory(list); list = NULL; }
+};
+
 
 #include "protos.h"
 #include "jcr.h"
index 5214c8a05e330a91e26568233b64e9dd1377d697..544ef803d5d5851921f312482f2ffb6f70ba3e08 100644 (file)
@@ -1,7 +1,7 @@
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
+   Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
 
    The main author of Bacula is Kern Sibbald, with contributions from
    many others, a complete list can be found in the file AUTHORS.
@@ -57,6 +57,7 @@ bool db_sql_query(B_DB *mdb, const char *cmd, DB_RESULT_HANDLER *result_handler,
 void db_start_transaction(JCR *jcr, B_DB *mdb);
 void db_end_transaction(JCR *jcr, B_DB *mdb);
 int db_int64_handler(void *ctx, int num_fields, char **row);
+int db_list_handler(void *ctx, int num_fields, char **row);
 void db_thread_cleanup();
 void _dbg_print_db(JCR *jcr, FILE *fp);
 int db_int_handler(void *ctx, int num_fields, char **row);
@@ -109,8 +110,8 @@ int db_get_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cdbr);
 int db_get_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr);
 bool db_get_query_dbids(JCR *jcr, B_DB *mdb, POOL_MEM &query, dbid_list &ids);
 bool db_get_file_list(JCR *jcr, B_DB *mdb, char *jobids, DB_RESULT_HANDLER *result_handler, void *ctx);
-bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM *jobids);
-int db_get_int_handler(void *ctx, int num_fields, char **row);
+bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb, JOB_DBR *jr, db_list_ctx *jobids);
+int db_get_int_handler(void *list, int num_fields, char **row);
 
 
 /* sql_list.c */
index 22eff8ef79d90432a71700802a7b25a564324cbe..49e9762010fb35ba1743c2396bf137613c67f740 100644 (file)
@@ -144,7 +144,21 @@ int db_int64_handler(void *ctx, int num_fields, char **row)
    return 0;
 }
 
-
+/*
+ * Use to build a comma separated list of values from a query. "10,20,30"
+ */
+int db_list_handler(void *ctx, int num_fields, char **row)
+{
+   db_list_ctx *lctx = (db_list_ctx *)ctx;
+   if (num_fields == 1 && row[0]) {
+      if (lctx->list[0]) {
+         pm_strcat(lctx->list, ",");
+      }
+      pm_strcat(lctx->list, row[0]);
+      lctx->count++;
+   }
+   return 0;
+}
 
 /* NOTE!!! The following routines expect that the
  *  calling subroutine sets and clears the mutex
index 6098bdfa32bbb5cd9170e7c0a7e147f9f00b55db..e83359e3bb3382ca856b81915ef863fd3e5a5274 100644 (file)
@@ -1,7 +1,7 @@
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
+   Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
 
    The main author of Bacula is Kern Sibbald, with contributions from
    many others, a complete list can be found in the file AUTHORS.
@@ -1099,7 +1099,7 @@ bool db_get_file_list(JCR *jcr, B_DB *mdb, char *jobids,
  * TODO: look and merge from ua_restore.c
  */
 bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb, 
-                            JOB_DBR *jr, POOLMEM *jobids)
+                            JOB_DBR *jr, db_list_ctx *jobids)
 {
    bool ret=false;
    char clientid[50], jobid[50], filesetid[50];
@@ -1110,7 +1110,8 @@ bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb,
    utime_t StartTime = (jr->StartTime)?jr->StartTime:time(NULL);
 
    bstrutime(date, sizeof(date),  StartTime + 1);
-   jobids[0]='\0';
+   jobids->list[0] = 0;
+   jobids->count = 0;
 
    /* First, find the last good Full backup for this job/client/fileset */
    Mmsg(query, 
@@ -1176,8 +1177,8 @@ bool db_accurate_get_jobids(JCR *jcr, B_DB *mdb,
 
    /* build a jobid list ie: 1,2,3,4 */
    Mmsg(query, "SELECT JobId FROM btemp3%s ORDER by JobTDate", jobid);
-   db_sql_query(mdb, query.c_str(), db_get_int_handler, jobids);
-   Dmsg1(1, "db_accurate_get_jobids=%s\n", jobids);
+   db_sql_query(mdb, query.c_str(), db_list_handler, jobids);
+   Dmsg1(1, "db_accurate_get_jobids=%s\n", jobids->list);
    ret = true;
 
 bail_out:
@@ -1187,19 +1188,4 @@ bail_out:
    return ret;
 }
 
-/*
- * Use to build a string of int list from a query. "10,20,30"
- */
-int db_get_int_handler(void *ctx, int num_fields, char **row)
-{
-   POOLMEM *ret = (POOLMEM *)ctx;
-   if (num_fields == 1 && row[0]) {
-      if (ret[0]) {
-         pm_strcat(ret, ",");
-      }
-      pm_strcat(ret, row[0]);
-   }
-   return 0;
-}
-
 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI */
index bcc02c912098176383ad94b62d3c3466d3b601b1..80f11ab10883f47169014ee13992a8f5df822ba5 100644 (file)
@@ -131,18 +131,15 @@ static int accurate_list_handler(void *ctx, int num_fields, char **row)
 bool send_accurate_current_files(JCR *jcr)
 {
    POOL_MEM buf;
-   POOLMEM *jobids;
-   POOLMEM *nb;
+   db_list_ctx jobids;
+   db_list_ctx nb;
 
    if (!jcr->accurate || job_canceled(jcr) || jcr->get_JobLevel()==L_FULL) {
       return true;
    }
-   jobids = get_pool_memory(PM_FNAME);
+   db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, &jobids);
 
-   db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, jobids);
-
-   if (*jobids == 0) {
-      free_pool_memory(jobids);
+   if (jobids.count == 0) {
       Jmsg(jcr, M_FATAL, 0, _("Cannot find previous jobids.\n"));
       return false;
    }
@@ -150,27 +147,20 @@ bool send_accurate_current_files(JCR *jcr)
       Jmsg(jcr, M_INFO, 0, _("Sending Accurate information.\n"));
    }
    /* to be able to allocate the right size for htable */
-   nb = get_pool_memory(PM_FNAME);
-   *nb = 0;                           /* clear buffer */
-   Mmsg(buf, "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)",jobids);
-   db_sql_query(jcr->db, buf.c_str(), db_get_int_handler, nb);
-   Dmsg2(200, "jobids=%s nb=%s\n", jobids, nb);
-   jcr->file_bsock->fsend("accurate files=%s\n", nb); 
+   Mmsg(buf, "SELECT sum(JobFiles) FROM Job WHERE JobId IN (%s)", jobids.list);
+   db_sql_query(jcr->db, buf.c_str(), db_list_handler, &nb);
+   Dmsg2(200, "jobids=%s nb=%s\n", jobids.list, nb.list);
+   jcr->file_bsock->fsend("accurate files=%s\n", nb.list); 
 
    if (!db_open_batch_connexion(jcr, jcr->db)) {
-      free_pool_memory(jobids);
-      free_pool_memory(nb);
       Jmsg0(jcr, M_FATAL, 0, "Can't get batch sql connexion");
       return false;
    }
 
-   db_get_file_list(jcr, jcr->db_batch, jobids, accurate_list_handler, (void *)jcr);
+   db_get_file_list(jcr, jcr->db_batch, jobids.list, accurate_list_handler, (void *)jcr);
 
    /* TODO: close the batch connexion ? (can be used very soon) */
 
-   free_pool_memory(jobids);
-   free_pool_memory(nb);
-
    jcr->file_bsock->signal(BNET_EOD);
 
    return true;
index 363d12fd8f453e03a4fd69bdfe049faedce8d843..c444efa0a3f6079649f6d52c492a4fa06de6e715 100644 (file)
@@ -156,15 +156,16 @@ bool do_a_dot_command(UAContext *ua)
 
 static bool dot_update(UAContext *ua, const char *cmd)
 {
+
    if (!open_client_db(ua)) {
       return 1;
    }
 
    int pos = find_arg_with_value(ua, "jobid");
-   if (pos != -1) {             /* find jobid arg */
-      if (is_a_number_list(ua->argv[pos])) {
-         bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, ua->argv[pos]);
-      }
+   if (pos != -1 && is_a_number_list(ua->argv[pos])) {
+      db_list_ctx jobids;
+      pm_strcpy(jobids.list, ua->argv[pos]);
+      bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, &jobids);
    } else {
       /* update cache for all jobids */
       bvfs_update_cache(ua->jcr, ua->db);
index c93ad9d2d42f36ba11d09859e1283a51fc10b42c..55c308966a248eb8b62d1ced01e628b5b7e7eb65 100644 (file)
@@ -483,7 +483,7 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
          }
          list_nextvol(ua, n);
       } else if (strcasecmp(ua->argk[i], NT_("copies")) == 0) {
-         char *jobids=NULL;
+         char *jobids = NULL;
          uint32_t limit=0;
          for (j=i+1; j<ua->argc; j++) {
             if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
index 5eae29b5c00a69105c8d5e62243673d676d1a119..e9e6a31e23cd0f6518d14e6ae12208d68ff02689 100644 (file)
@@ -1,7 +1,7 @@
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2002-2008 Free Software Foundation Europe e.V.
+   Copyright (C) 2002-2009 Free Software Foundation Europe e.V.
 
    The main author of Bacula is Kern Sibbald, with contributions from
    many others, a complete list can be found in the file AUTHORS.
@@ -556,6 +556,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
       char *fname;
       int len;
       bool gui_save;
+      db_list_ctx jobids;
 
       start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
       for (int i=0; list[i]; i++) {
@@ -752,9 +753,10 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
             return 0;
          }
          jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */
-         if (!db_accurate_get_jobids(ua->jcr, ua->db, &jr, rx->JobIds)) {
+         if (!db_accurate_get_jobids(ua->jcr, ua->db, &jr, &jobids)) {
             return 0;
          }
+         pm_strcpy(rx->JobIds, jobids.list);
          Dmsg1(30, "Item 12: jobids = %s\n", rx->JobIds);
          break;
       case 12:                        /* Cancel or quit */
index 45a1f7e06522e6bbe41fc02365ca9c16840ea3b2..e75d08d06c5e2a1948c2ad7f5be14543d88f8433 100644 (file)
@@ -50,7 +50,7 @@
 
 static const int dbglevel = 10;
 
-static bool create_bootstrap_file(JCR *jcr, POOLMEM *jobids);
+static bool create_bootstrap_file(JCR *jcr, char *jobids);
 void vbackup_cleanup(JCR *jcr, int TermCode);
 
 /* 
@@ -135,6 +135,7 @@ bool do_vbackup(JCR *jcr)
    char ed1[100];
    BSOCK *sd;
    char *p;
+   db_list_ctx jobids;
 
    Dmsg2(100, "rstorage=%p wstorage=%p\n", jcr->rstorage, jcr->wstorage);
    Dmsg2(100, "Read store=%s, write store=%s\n", 
@@ -157,28 +158,27 @@ bool do_vbackup(JCR *jcr)
 _("This Job is not an Accurate backup so is not equivalent to a Full backup.\n"));
    }
 
-   POOLMEM *jobids = get_pool_memory(PM_FNAME);
    jcr->jr.JobLevel = L_VIRTUAL_FULL;
-   db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, jobids);
-   jcr->jr.JobLevel = L_FULL;
-   Dmsg1(10, "Accurate jobids=%s\n", jobids);
-   if (*jobids == 0) {
-      free_pool_memory(jobids);
+   db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, &jobids);
+   Dmsg1(10, "Accurate jobids=%s\n", jobids.list);
+   if (jobids.count == 0) {
       Jmsg(jcr, M_FATAL, 0, _("No previous Jobs found.\n"));
       return false;
    }
 
+   jcr->jr.JobLevel = L_FULL;
+
    /*
     * Now we find the last job that ran and store it's info in
     *  the previous_jr record.  We will set our times to the
     *  values from that job so that anything changed after that
     *  time will be picked up on the next backup.
     */
-   p = strrchr(jobids, ',');              /* find last jobid */
+   p = strrchr(jobids.list, ',');           /* find last jobid */
    if (p != NULL) {
       p++;
    } else {
-      p = jobids;
+      p = jobids.list;
    }
    memset(&jcr->previous_jr, 0, sizeof(jcr->previous_jr));
    jcr->previous_jr.JobId = str_to_int64(p);
@@ -189,12 +189,10 @@ _("This Job is not an Accurate backup so is not equivalent to a Full backup.\n")
       return false;
    }
 
-   if (!create_bootstrap_file(jcr, jobids)) {
+   if (!create_bootstrap_file(jcr, jobids.list)) {
       Jmsg(jcr, M_FATAL, 0, _("Could not get or create the FileSet record.\n"));
-      free_pool_memory(jobids);
       return false;
    }
-   free_pool_memory(jobids);
 
    /*
     * Open a message channel connection with the Storage
@@ -476,7 +474,7 @@ int insert_bootstrap_handler(void *ctx, int num_fields, char **row)
 }
 
 
-static bool create_bootstrap_file(JCR *jcr, POOLMEM *jobids)
+static bool create_bootstrap_file(JCR *jcr, char *jobids)
 {
    RESTORE_CTX rx;
    UAContext *ua;
index 8b13435f5fa62726ff20864edffe8dbabe8cfc98..a26ca61aba1a08f5bc0c08f7cde5a30a020bb48d 100644 (file)
@@ -1,14 +1,7 @@
-/*
- *   edit.c  edit string to ascii, and ascii to internal
- *
- *    Kern Sibbald, December MMII
- *
- *   Version $Id$
- */
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2002-2006 Free Software Foundation Europe e.V.
+   Copyright (C) 2002-2009 Free Software Foundation Europe e.V.
 
    The main author of Bacula is Kern Sibbald, with contributions from
    many others, a complete list can be found in the file AUTHORS.
    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
    Switzerland, email:ftf@fsfeurope.org.
 */
+/*
+ *   edit.c  edit string to ascii, and ascii to internal
+ *
+ *    Kern Sibbald, December MMII
+ *
+ *   Version $Id$
+ */
 
 #include "bacula.h"
 #include <math.h>
@@ -407,7 +407,7 @@ bool is_a_number(const char *n)
 }
 
 /*
- * Check if specified string is a list of number or not
+ * Check if specified string is a list of numbers or not
  */
 bool is_a_number_list(const char *n)
 {
index 973821347148dcb4f2bd0bccc5cc9b9fa3e2b86d..1d0512d983f83060528180b33e3561a7495cbdbc 100644 (file)
@@ -2,6 +2,8 @@
           
 General:
 
+30Aug09
+kes  Fix bug #1355 Director crashes with double free in Accurate SQL query
 28Aug09
 kes  Fix bug #1357 Verify jobs fail when job has zero files
 26Aug09