add bug closure to changelog
[debian/amanda] / server-src / driver.c
index 9a0bb27bceba68fa9c80773cb6c6e0cbf4587458..af5095ab93a8715c3a50fee552bcb1c1aca19b89 100644 (file)
@@ -76,6 +76,7 @@ static int  inparallel;
 static int nodump = 0;
 static off_t tape_length = (off_t)0;
 static int current_tape = 0;
+static int conf_max_dle_by_volume;
 static int conf_taperalgo;
 static int conf_taper_parallel_write;
 static int conf_runtapes;
@@ -140,7 +141,7 @@ static void start_degraded_mode(disklist_t *queuep);
 static void start_some_dumps(disklist_t *rq);
 static void continue_port_dumps(void);
 static void update_failed_dump(disk_t *);
-static int all_taper_idle(void);
+static int no_taper_flushing(void);
 
 typedef enum {
     TAPE_ACTION_NO_ACTION         = 0,
@@ -199,6 +200,12 @@ main(
     find_result_t *holding_files;
     disklist_t holding_disklist = { NULL, NULL };
     int no_taper = FALSE;
+    int from_client = FALSE;
+
+    if (argc > 1 && argv && argv[1] && g_str_equal(argv[1], "--version")) {
+       printf("driver-%s\n", VERSION);
+       return (0);
+    }
 
     /*
      * Configure program for internationalization:
@@ -267,6 +274,15 @@ main(
        }
     }
 
+    if (argc > 2) {
+       if (strcmp(argv[2], "--from-client") == 0) {
+           from_client = TRUE;
+           from_client = from_client;
+           argv++;
+           argc--;
+       }
+    }
+
     safe_cd(); /* do this *after* config_init */
 
     check_running_as(RUNNING_AS_DUMPUSER);
@@ -326,6 +342,7 @@ main(
     conf_taper_parallel_write = getconf_int(CNF_TAPER_PARALLEL_WRITE);
     conf_tapetype = getconf_str(CNF_TAPETYPE);
     conf_runtapes = getconf_int(CNF_RUNTAPES);
+    conf_max_dle_by_volume = getconf_int(CNF_MAX_DLE_BY_VOLUME);
     if (conf_taper_parallel_write > conf_runtapes) {
        conf_taper_parallel_write = conf_runtapes;
     }
@@ -335,7 +352,6 @@ main(
     conf_flush_threshold_dumped = getconf_int(CNF_FLUSH_THRESHOLD_DUMPED);
     conf_flush_threshold_scheduled = getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED);
     conf_taperflush = getconf_int(CNF_TAPERFLUSH);
-
     flush_threshold_dumped = (conf_flush_threshold_dumped * tape_length) / 100;
     flush_threshold_scheduled = (conf_flush_threshold_scheduled * tape_length) / 100;
     taperflush = (conf_taperflush *tape_length) / 100;
@@ -499,31 +515,11 @@ main(
        headqueue_disk(&directq, diskp);
     }
 
-    /* handle any remaining dumps by dumping directly to tape, if possible */
-    while(!empty(directq) && taper_fd > 0) {
-       time_t  sleep_time  = 100000000;
-       disk_t *sleep_diskp = NULL;
-       time_t  now         = time(0);
-
-       /* Find one we can do immediately or the sonner */
-       for (diskp = directq.head; diskp != NULL; diskp = diskp->next) {
-           if (diskp->to_holdingdisk == HOLD_REQUIRED ||
-               degraded_mode) {
-               sleep_time = 0;
-               sleep_diskp = diskp;
-           } else if (diskp->host->start_t - now < sleep_time &&
-                      diskp->start_t -now < sleep_time) {
-               if (diskp->host->start_t > diskp->start_t)
-                   sleep_time = diskp->host->start_t - now;
-               else
-                   sleep_time = diskp->start_t - now;
-               sleep_diskp = diskp;
-           }
-       }
-       diskp = sleep_diskp;
-       if (sleep_time > 0)
-           sleep(sleep_time);
-       remove_disk(&directq, diskp);
+    run_server_global_scripts(EXECUTE_ON_POST_BACKUP, get_config_name());
+
+    /* log error for any remaining dumps */
+    while(!empty(directq)) {
+       diskp = dequeue_disk(&directq);
 
        if (diskp->to_holdingdisk == HOLD_REQUIRED) {
            char *qname = quote_string(diskp->name);
@@ -538,7 +534,7 @@ main(
            log_add(L_FAIL, "%s %s %s %d [%s]",
                diskp->host->hostname, qname, sched(diskp)->datestamp,
                sched(diskp)->level,
-               _("can't dump in degraded mode"));
+               _("can't dump in non degraded mode"));
            amfree(qname);
        }
        else {
@@ -555,10 +551,6 @@ main(
        }
     }
 
-    /* fill up the tape or start new one for taperflush */
-    startaflush();
-    event_loop(0);
-
     short_dump_state();                                /* for amstatus */
 
     g_printf(_("driver: QUITTING time %s telling children to quit\n"),
@@ -735,46 +727,51 @@ wait_for_children(void)
 
 }
 
-static void startaflush_tape(taper_t *taper);
+static void startaflush_tape(taper_t *taper, gboolean *state_changed);
 
 static void
 startaflush(void)
 {
     taper_t *taper;
+    gboolean state_changed = FALSE;
 
     for(taper = tapetable; taper <= tapetable+conf_taper_parallel_write;
        taper++) {
        if (!(taper->state & TAPER_STATE_DONE) &&
            taper->state & TAPER_STATE_WAIT_FOR_TAPE) {
-           startaflush_tape(taper);
+           startaflush_tape(taper, &state_changed);
        }
     }
     for(taper = tapetable; taper <= tapetable+conf_taper_parallel_write;
        taper++) {
        if (!(taper->state & TAPER_STATE_DONE) &&
            taper->state & TAPER_STATE_TAPE_REQUESTED) {
-           startaflush_tape(taper);
+           startaflush_tape(taper, &state_changed);
        }
     }
     for(taper = tapetable; taper <= tapetable+conf_taper_parallel_write;
        taper++) {
        if (!(taper->state & TAPER_STATE_DONE) &&
            taper->state & TAPER_STATE_INIT) {
-           startaflush_tape(taper);
+           startaflush_tape(taper, &state_changed);
        }
     }
     for(taper = tapetable; taper <= tapetable+conf_taper_parallel_write;
        taper++) {
        if (!(taper->state & TAPER_STATE_DONE) &&
            taper->state & TAPER_STATE_IDLE) {
-           startaflush_tape(taper);
+           startaflush_tape(taper, &state_changed);
        }
     }
+    if (state_changed) {
+       short_dump_state();
+    }
 }
 
 static void
 startaflush_tape(
-    taper_t *taper)
+    taper_t  *taper,
+    gboolean *state_changed)
 {
     disk_t *dp = NULL;
     disk_t *fit = NULL;
@@ -803,6 +800,7 @@ startaflush_tape(
        taper_cmd(NO_NEW_TAPE, taper->disk, why_no_new_tape, 0, NULL);
        taper->state |= TAPER_STATE_DONE;
        start_degraded_mode(&runq);
+       *state_changed = TRUE;
     } else if (result_tape_action & TAPE_ACTION_MOVE) {
        taper_t *taper1 = idle_taper();
        if (taper1) {
@@ -812,9 +810,11 @@ startaflush_tape(
            taper1->state = TAPER_STATE_DEFAULT;
            taper->state |= TAPER_STATE_TAPE_STARTED;
            taper->left = taper1->left;
+           taper->nb_dle++;
            if (last_started_taper == taper1) {
                last_started_taper = taper;
            }
+           *state_changed = TRUE;
        }
     }
 
@@ -940,6 +940,24 @@ startaflush_tape(
                if(dp) remove_disk(&tapeq, dp);
                break;
        }
+       if (!dp) {
+           if (!(result_tape_action & TAPE_ACTION_START_A_FLUSH_FIT)) {
+               if(conf_taperalgo != ALGO_SMALLEST)  {
+                   g_fprintf(stderr,
+                       _("driver: startaflush: Using SMALLEST because nothing fit\n"));
+               }
+               
+               fit = dp = tapeq.head;
+               while (fit != NULL) {
+                   if (sched(fit)->act_size < sched(dp)->act_size &&
+                       strcmp(sched(fit)->datestamp, datestamp) <= 0) {
+                       dp = fit;
+                   }
+                   fit = fit->next;
+               }
+               if(dp) remove_disk(&tapeq, dp);
+           }
+       }
        if (dp) {
            taper->disk = dp;
            taper->dumper = NULL;
@@ -958,6 +976,7 @@ startaflush_tape(
                                               handle_taper_result, NULL);
            }
            taper_nb_wait_reply++;
+           taper->nb_dle++;
            sched(dp)->taper = taper;
            taper_cmd(FILE_WRITE, dp, sched(dp)->destname, sched(dp)->level,
                      sched(dp)->datestamp);
@@ -966,8 +985,8 @@ startaflush_tape(
                    (long long)sched(taper->disk)->act_size,
                    (long long)taper->left);
            amfree(qname);
+           *state_changed = TRUE;
        }
-       short_dump_state();
     }
 }
 
@@ -1038,12 +1057,13 @@ allow_dump_dle(
     } else if (!taper && (holdp =
        find_diskspace(sched(diskp)->est_size, cur_idle, NULL)) == NULL) {
        *cur_idle = max(*cur_idle, IDLE_NO_DISKSPACE);
-       if (empty(tapeq) && dumper_to_holding == 0 && rq != &directq) {
+       if (empty(tapeq) && dumper_to_holding == 0 && rq != &directq && no_taper_flushing()) {
            remove_disk(rq, diskp);
            if (diskp->to_holdingdisk != HOLD_REQUIRED) {
                enqueue_disk(&directq, diskp);
                diskp->to_holdingdisk = HOLD_NEVER;
            }
+           if (empty(*rq)) force_flush = 1;
        }
     } else if (client_constrained(diskp)) {
        free_assignedhd(holdp);
@@ -1094,7 +1114,7 @@ start_some_dumps(
 {
     const time_t now = time(NULL);
     int cur_idle;
-    disk_t *diskp, *delayed_diskp, *diskp_accept;
+    disk_t *diskp, *delayed_diskp, *diskp_accept, *diskp_next;
     disk_t *dp;
     assignedhd_t **holdp=NULL, **holdp_accept;
     cmd_t cmd;
@@ -1106,6 +1126,7 @@ start_some_dumps(
     char dumptype;
     char *dumporder;
     int  dumper_to_holding = 0;
+    gboolean state_changed = FALSE;
 
     /* don't start any actual dumps until the taper is started */
     if (!taper_started) return;
@@ -1201,7 +1222,8 @@ start_some_dumps(
                    }
 
                    for (diskp = directq.head; diskp != NULL;
-                                              diskp = diskp->next) {
+                                              diskp = diskp_next) {
+                       diskp_next = diskp->next;
                        allow_dump_dle(diskp, taper, dumptype, &directq, now,
                                       dumper_to_holding, &cur_idle,
                                       &delayed_diskp, &diskp_accept,
@@ -1220,7 +1242,8 @@ start_some_dumps(
        }
 
        if (diskp == NULL) {
-           for(diskp = rq->head; diskp != NULL; diskp = diskp->next) {
+           for(diskp = rq->head; diskp != NULL; diskp = diskp_next) {
+               diskp_next = diskp->next;
                assert(diskp->host != NULL && sched(diskp) != NULL);
 
                allow_dump_dle(diskp, NULL, dumptype, rq, now,
@@ -1314,18 +1337,17 @@ start_some_dumps(
                diskp->dataport_list = stralloc(result_argv[2]);
 
                if (diskp->host->pre_script == 0) {
-                   for (dp=diskp->host->disks; dp != NULL; dp = dp->hostnext) {
-                       run_server_scripts(EXECUTE_ON_PRE_HOST_BACKUP,
-                                          get_config_name(), dp, -1);
-                   }
+                   run_server_host_scripts(EXECUTE_ON_PRE_HOST_BACKUP,
+                                           get_config_name(), diskp->host);
                    diskp->host->pre_script = 1;
                }
-               run_server_scripts(EXECUTE_ON_PRE_DLE_BACKUP,
+               run_server_dle_scripts(EXECUTE_ON_PRE_DLE_BACKUP,
                                   get_config_name(), diskp,
                                   sched(diskp)->level);
                dumper_cmd(dumper, PORT_DUMP, diskp, NULL);
            }
            diskp->host->start_t = now + 15;
+           if (empty(*rq)) force_flush = 1;
 
            if (result_argv)
                g_strfreev(result_argv);
@@ -1359,6 +1381,7 @@ start_some_dumps(
            taper->dumper = dumper;
            taper->state |= TAPER_STATE_DUMP_TO_TAPE;
            taper->state &= ~TAPER_STATE_IDLE;
+           taper->nb_dle++;
            if (taper_nb_wait_reply == 0) {
                taper_ev_read = event_register(taper_fd, EV_READFD,
                                               handle_taper_result, NULL);
@@ -1369,9 +1392,12 @@ start_some_dumps(
                      sched(diskp)->datestamp);
            diskp->host->start_t = now + 15;
 
-           short_dump_state();
+           state_changed = TRUE;
        }
     }
+    if (state_changed) {
+       short_dump_state();
+    }
 }
 
 /*
@@ -1509,15 +1535,17 @@ continue_port_dumps(void)
        if( dumper->busy ) {
            busy_dumpers++;
            if( !find_disk(&roomq, dumper->dp) ) {
-               active_dumpers++;
-           } else if( !dp || 
+               if (dumper->chunker) {
+                   active_dumpers++;
+               }
+           } else if( !dp ||
                       sched(dp)->est_size > sched(dumper->dp)->est_size ) {
                dp = dumper->dp;
            }
        }
     }
-    if((dp != NULL) && (active_dumpers == 0) && (busy_dumpers > 0) && 
-        ((all_taper_idle() && empty(tapeq)) || degraded_mode) &&
+    if((dp != NULL) && (active_dumpers == 0) && (busy_dumpers > 0) &&
+        ((no_taper_flushing() && empty(tapeq)) || degraded_mode) &&
        pending_aborts == 0 ) { /* case b */
        sched(dp)->no_space = 1;
        /* At this time, dp points to the dump with the smallest est_size.
@@ -1535,7 +1563,7 @@ static void
 handle_taper_result(
        void *cookie G_GNUC_UNUSED)
 {
-    disk_t *dp = NULL, *dp1;
+    disk_t *dp = NULL;
     dumper_t *dumper;
     cmd_t cmd;
     int result_argc;
@@ -1573,6 +1601,7 @@ handle_taper_result(
            }
            assert(taper != NULL);
            taper->left = 0;
+           taper->nb_dle = 0;
            taper->state &= ~TAPER_STATE_INIT;
            taper->state |= TAPER_STATE_RESERVATION;
            taper->state |= TAPER_STATE_IDLE;
@@ -1607,6 +1636,9 @@ handle_taper_result(
 
            if (strcmp(result_argv[2], "INPUT-ERROR") == 0) {
                taper->input_error = newstralloc(taper->input_error, result_argv[4]);
+               taper->result = FAILED;
+               amfree(qname);
+               break;
            } else if (strcmp(result_argv[2], "INPUT-GOOD") != 0) {
                taper->tape_error = newstralloc(taper->tape_error,
                                               _("Taper protocol error"));
@@ -1620,6 +1652,9 @@ handle_taper_result(
            if (strcmp(result_argv[3], "TAPE-ERROR") == 0) {
                taper->state &= ~TAPER_STATE_TAPE_STARTED;
                taper->tape_error = newstralloc(taper->tape_error, result_argv[5]);
+               taper->result = FAILED;
+               amfree(qname);
+               break;
            } else if (strcmp(result_argv[3], "TAPE-GOOD") != 0) {
                taper->state &= ~TAPER_STATE_TAPE_STARTED;
                taper->tape_error = newstralloc(taper->tape_error,
@@ -1657,6 +1692,9 @@ handle_taper_result(
 
            if (strcmp(result_argv[2], "INPUT-ERROR") == 0) {
                taper->input_error = newstralloc(taper->input_error, result_argv[5]);
+               taper->result = FAILED;
+               amfree(qname);
+               break;
            } else if (strcmp(result_argv[2], "INPUT-GOOD") != 0) {
                taper->tape_error = newstralloc(taper->tape_error,
                                               _("Taper protocol error"));
@@ -1670,6 +1708,9 @@ handle_taper_result(
            if (strcmp(result_argv[3], "TAPE-ERROR") == 0) {
                taper->state &= ~TAPER_STATE_TAPE_STARTED;
                taper->tape_error = newstralloc(taper->tape_error, result_argv[6]);
+               taper->result = FAILED;
+               amfree(qname);
+               break;
            } else if (strcmp(result_argv[3], "TAPE-GOOD") != 0) {
                taper->state &= ~TAPER_STATE_TAPE_STARTED;
                taper->tape_error = newstralloc(taper->tape_error,
@@ -1686,6 +1727,12 @@ handle_taper_result(
            if (s) {
                s += 4;
                sched(dp)->dumpsize = atol(s);
+           } else {
+               s = strstr(result_argv[4], " bytes ");
+               if (s) {
+                   s += 7;
+                   sched(dp)->dumpsize = atol(s)/1024;
+               }
            }
 
            taper->result = cmd;
@@ -1716,6 +1763,12 @@ handle_taper_result(
            if (s) {
                s += 4;
                partsize = atol(s);
+           } else {
+               s = strstr(result_argv[5], " bytes ");
+               if (s) {
+                   s += 7;
+                   partsize = atol(s)/1024;
+               }
            }
            taper->left -= partsize;
 
@@ -1730,11 +1783,15 @@ handle_taper_result(
 
            dp = serial2disk(result_argv[1]);
            taper = sched(dp)->taper;
-           taper->state &= ~TAPER_STATE_TAPE_STARTED;
-           taper->state |= TAPER_STATE_TAPE_REQUESTED;
+           if (taper->state & TAPER_STATE_DONE) {
+               taper_cmd(NO_NEW_TAPE, taper->disk, "taper found no tape", 0, NULL);
+           } else {
+               taper->state &= ~TAPER_STATE_TAPE_STARTED;
+               taper->state |= TAPER_STATE_TAPE_REQUESTED;
 
-           start_some_dumps(&runq);
-           startaflush();
+               start_some_dumps(&runq);
+               startaflush();
+           }
            break;
 
        case NEW_TAPE: /* NEW-TAPE <handle> <label> */
@@ -1750,6 +1807,7 @@ handle_taper_result(
            taper = sched(dp)->taper;
             /* Update our tape counter and reset taper->left */
            current_tape++;
+           taper->nb_dle = 1;
            taper->left = tape_length;
            taper->state &= ~TAPER_STATE_WAIT_NEW_TAPE;
            taper->state |= TAPER_STATE_TAPE_STARTED;
@@ -1854,13 +1912,11 @@ handle_taper_result(
            taper->state |= TAPER_STATE_DUMP_TO_TAPE;
 
            if (dp->host->pre_script == 0) {
-               for (dp1=dp->host->disks; dp1 != NULL; dp1 = dp1->hostnext) {
-                   run_server_scripts(EXECUTE_ON_PRE_HOST_BACKUP,
-                                      get_config_name(), dp1, -1);
-               }
+               run_server_host_scripts(EXECUTE_ON_PRE_HOST_BACKUP,
+                                       get_config_name(), dp->host);
                dp->host->pre_script = 1;
            }
-           run_server_scripts(EXECUTE_ON_PRE_DLE_BACKUP,
+           run_server_dle_scripts(EXECUTE_ON_PRE_DLE_BACKUP,
                               get_config_name(), dp,
                               sched(dp)->level);
            /* tell the dumper to dump to a port */
@@ -1884,6 +1940,7 @@ handle_taper_result(
              * checks. If there are dumps waiting for diskspace to be freed,
              * cancel one.
              */
+           taper_started = 1;
             if(!nodump) {
                 log_add(L_WARNING,
                         _("going into degraded mode because of taper component error."));
@@ -1892,7 +1949,7 @@ handle_taper_result(
            for (taper = tapetable;
                 taper < tapetable + conf_taper_parallel_write;
                  taper++) {
-               if (taper && taper->disk && taper->result != LAST_TOK) {
+               if (taper && taper->disk) {
                    taper->tape_error = newstralloc(taper->tape_error,"BOGUS");
                    taper->result = cmd;
                    if (taper->dumper) {
@@ -1928,6 +1985,9 @@ handle_taper_result(
        g_strfreev(result_argv);
 
        if (taper && taper->disk && taper->result != LAST_TOK) {
+           if (taper->nb_dle >= conf_max_dle_by_volume) {
+               taper_cmd(CLOSE_VOLUME, dp, NULL, 0, NULL);
+           }
            if(taper->dumper) {
                if (taper->dumper->result != LAST_TOK) {
                    // Dumper already returned it's result
@@ -1939,6 +1999,7 @@ handle_taper_result(
        }
 
     } while(areads_dataready(taper_fd));
+    start_some_dumps(&runq);
     startaflush();
 }
 
@@ -2060,7 +2121,6 @@ dumper_taper_result(
 {
     dumper_t *dumper;
     taper_t  *taper;
-    int is_partial;
     char *qname;
 
     dumper = sched(dp)->dumper;
@@ -2085,8 +2145,6 @@ dumper_taper_result(
        update_failed_dump(dp);
     }
 
-    is_partial = dumper->result != DONE || taper->result != DONE;
-
     sched(dp)->dump_attempted += 1;
     sched(dp)->taper_attempted += 1;
 
@@ -2114,6 +2172,7 @@ dumper_taper_result(
     dp->inprogress = 0;
     deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
     taper->dumper = NULL;
+    taper->disk = NULL;
     sched(dp)->dumper = NULL;
     sched(dp)->taper = NULL;
     start_some_dumps(&runq);
@@ -2375,7 +2434,7 @@ handle_dumper_result(
            int last_dump = 1;
            dumper_t *dumper;
 
-           run_server_scripts(EXECUTE_ON_POST_DLE_BACKUP,
+           run_server_dle_scripts(EXECUTE_ON_POST_DLE_BACKUP,
                               get_config_name(), dp, sched(dp)->level);
            /* check dump not yet started */
            for (dp1=runq.head; dp1 != NULL; dp1 = dp1->next) {
@@ -2395,10 +2454,8 @@ handle_dumper_result(
            }
            if (last_dump && dp->host->post_script == 0) {
                if (dp->host->post_script == 0) {
-                   for (dp1=dp->host->disks; dp1 != NULL; dp1 = dp1->hostnext) {
-                       run_server_scripts(EXECUTE_ON_POST_HOST_BACKUP,
-                                          get_config_name(), dp1, -1);
-                   }
+                   run_server_host_scripts(EXECUTE_ON_POST_HOST_BACKUP,
+                                           get_config_name(), dp->host);
                    dp->host->post_script = 1;
                }
            }
@@ -2420,14 +2477,12 @@ handle_dumper_result(
                dumper->chunker->result != LAST_TOK)
                dumper_chunker_result(dp);
        } else { /* send the dumper result to the taper */
-           if (taper->sendresult) {
-               if (cmd == DONE) {
-                   taper_cmd(DONE, dp, NULL, 0, NULL);
-               } else {
-                   taper_cmd(FAILED, dp, NULL, 0, NULL);
-               }
-               taper->sendresult = 0;
+           if (cmd == DONE) {
+               taper_cmd(DONE, dp, NULL, 0, NULL);
+           } else {
+               taper_cmd(FAILED, dp, NULL, 0, NULL);
            }
+           taper->sendresult = 0;
            if (taper->dumper && taper->result != LAST_TOK) {
                dumper_taper_result(dp);
            }
@@ -2759,7 +2814,7 @@ read_flush(
            continue;
        }
 
-       if(file.dumplevel < 0 || file.dumplevel > 9) {
+       if (file.dumplevel < 0 || file.dumplevel > 399) {
            log_add(L_INFO, _("%s: ignoring file with bogus dump level %d."),
                    destname, file.dumplevel);
            amfree(destname);
@@ -2825,6 +2880,8 @@ read_flush(
     if (!nodump) {
        schedule_ev_read = event_register((event_id_t)0, EV_READFD,
                                          read_schedule, NULL);
+    } else {
+       force_flush = 1;
     }
 }
 
@@ -3137,6 +3194,8 @@ read_schedule(
        log_add(L_WARNING, _("WARNING: got empty schedule from planner"));
     if(need_degraded==1) start_degraded_mode(&runq);
     schedule_done = 1;
+    run_server_global_scripts(EXECUTE_ON_PRE_BACKUP, get_config_name());
+    if (empty(runq)) force_flush = 1;
     start_some_dumps(&runq);
     startaflush();
 }
@@ -3534,10 +3593,18 @@ build_diskspace(
                break;
            }
        }
-
+       if (!ha || j >= num_holdalloc) {
+           fprintf(stderr,_("build_diskspace: holding disk file '%s' is not in a holding disk directory.\n"), filename);
+           amfree(used);
+           amfree(result);
+           return NULL;
+       }
        if(stat(filename, &finfo) == -1) {
-           g_fprintf(stderr, _("stat %s: %s\n"), filename, strerror(errno));
-           finfo.st_size = (off_t)0;
+           g_fprintf(stderr, _("build_diskspace: can't stat %s: %s\n"),
+                     filename, strerror(errno));
+           amfree(used);
+           amfree(result);
+           return NULL;
        }
        used[j] += ((off_t)finfo.st_size+(off_t)1023)/(off_t)1024;
        if((fd = open(filename,O_RDONLY)) == -1) {
@@ -3673,30 +3740,33 @@ tape_action(
     off_t sched_size;
     off_t dump_to_disk_size;
     int   dump_to_disk_terminated;
-    off_t my_flush_threshold_dumped;
-    off_t my_flush_threshold_scheduled;
-    off_t my_taperflush;
     int   nb_taper_active = nb_sent_new_tape;
     int   nb_taper_flushing = 0;
+    int   dle_free = 0;
+    off_t data_next_tape = 0;
+    off_t data_free = 0;
+    off_t data_lost = 0;
+    off_t data_lost_next_tape = 0;
+    gboolean allow_size_or_number;
 
     dumpers_size = 0;
     for(dumper = dmptable; dumper < (dmptable+inparallel); dumper++) {
        if (dumper->busy && !sched(dumper->dp)->taper)
            dumpers_size += sched(dumper->dp)->est_size;
     }
-    driver_debug(1, _("dumpers_size: %lld\n"), (long long)dumpers_size);
+    driver_debug(2, _("dumpers_size: %lld\n"), (long long)dumpers_size);
 
     runq_size = 0;
     for(dp = runq.head; dp != NULL; dp = dp->next) {
        runq_size += sched(dp)->est_size;
     }
-    driver_debug(1, _("runq_size: %lld\n"), (long long)runq_size);
+    driver_debug(2, _("runq_size: %lld\n"), (long long)runq_size);
 
     directq_size = 0;
     for(dp = directq.head; dp != NULL; dp = dp->next) {
        directq_size += sched(dp)->est_size;
     }
-    driver_debug(1, _("directq_size: %lld\n"), (long long)directq_size);
+    driver_debug(2, _("directq_size: %lld\n"), (long long)directq_size);
 
     tapeq_size = directq_size;
     for(dp = tapeq.head; dp != NULL; dp = dp->next) {
@@ -3716,22 +3786,43 @@ tape_action(
         taper1++) {
        if (taper1->state & TAPER_STATE_TAPE_STARTED) {
            tapeq_size -= taper1->left;
+           dle_free += (conf_max_dle_by_volume - taper1->nb_dle);
        }
        if (taper1->disk) {
+           off_t data_to_go;
            if (taper1->dumper) {
-               tapeq_size += sched(taper1->disk)->est_size - taper1->written;
+               data_to_go = sched(taper1->disk)->est_size - taper1->written;
+           } else {
+               data_to_go = sched(taper1->disk)->act_size - taper1->written;
+           }
+           if (data_to_go > taper1->left) {
+               data_next_tape += data_to_go - taper1->left;
+               data_lost += taper1->written + taper1->left;
+               if (taper1->state & TAPER_STATE_TAPE_STARTED) {
+                   dle_free--;
+               } else {
+                   dle_free += conf_max_dle_by_volume - 2;
+               }
            } else {
-               tapeq_size += sched(taper1->disk)->act_size - taper1->written;
+               data_free += taper1->left - data_to_go;
            }
+           tapeq_size += data_to_go;
        }
     }
-    driver_debug(1, _("tapeq_size: %lld\n"), (long long)tapeq_size);
+    data_lost_next_tape = tape_length + data_free - data_next_tape - runq_size - directq_size - tapeq_size;
+    driver_debug(2, _("dle_free: %d\n"), dle_free);
+    driver_debug(2, _("data_lost: %lld\n"), (long long)data_lost);
+    driver_debug(2, _("data_free: %lld\n"), (long long)data_free);
+    driver_debug(2, _("data_next_tape: %lld\n"), (long long)data_next_tape);
+    driver_debug(2, _("data_lost_next_tape: %lld\n"), (long long)data_lost_next_tape);
+;
+    driver_debug(2, _("tapeq_size: %lld\n"), (long long)tapeq_size);
 
     sched_size = runq_size + directq_size + tapeq_size + dumpers_size;
-    driver_debug(1, _("sched_size: %lld\n"), (long long)sched_size);
+    driver_debug(2, _("sched_size: %lld\n"), (long long)sched_size);
 
     dump_to_disk_size = dumpers_size + runq_size + directq_size;
-    driver_debug(1, _("dump_to_disk_size: %lld\n"), (long long)dump_to_disk_size);
+    driver_debug(2, _("dump_to_disk_size: %lld\n"), (long long)dump_to_disk_size);
 
     dump_to_disk_terminated = schedule_done && dump_to_disk_size == 0;
 
@@ -3741,24 +3832,22 @@ tape_action(
            nb_taper_active++;
        }
     }
-    if (nb_taper_active >= 1) {
-    my_flush_threshold_dumped = flush_threshold_dumped +
-                               (nb_taper_active-nb_taper_active) * tape_length;
-    my_flush_threshold_scheduled = flush_threshold_scheduled +
-                                  (nb_taper_active-nb_taper_active) * tape_length;
-    my_taperflush = taperflush + (nb_taper_active-nb_taper_active) * tape_length;
-    } else {
-    my_flush_threshold_dumped = flush_threshold_dumped +
-                               nb_taper_active * tape_length;
-    my_flush_threshold_scheduled = flush_threshold_scheduled +
-                                  nb_taper_active * tape_length;
-    my_taperflush = taperflush + nb_taper_active * tape_length;
-    }
+
+    allow_size_or_number = (flush_threshold_dumped < tapeq_size &&
+                           flush_threshold_scheduled < sched_size) ||
+                          (dle_free < (queue_length(runq) +
+                                       queue_length(directq) +
+                                       queue_length(tapeq)));
+    driver_debug(2, "queue_length(runq) %d\n", queue_length(runq));
+    driver_debug(2, "queue_length(directq) %d\n", queue_length(directq));
+    driver_debug(2, "queue_length(tapeq) %d\n", queue_length(tapeq));
+    driver_debug(2, "allow_size_or_number %d\n", allow_size_or_number);
 
     // Changing conditionals can produce a driver hang, take care.
     // 
     // when to start writting to a new tape
     if (taper->state & TAPER_STATE_TAPE_REQUESTED) {
+       driver_debug(2, "tape_action: TAPER_STATE_TAPE_REQUESTED\n");
        if (current_tape >= conf_runtapes && taper_nb_scan_volume == 0 &&
            nb_taper_active == 0) {
            *why_no_new_tape = g_strdup_printf(_("%d tapes filled; runtapes=%d "
@@ -3766,8 +3855,8 @@ tape_action(
            result |= TAPE_ACTION_NO_NEW_TAPE;
        } else if (current_tape < conf_runtapes &&
                   taper_nb_scan_volume == 0 &&
-                  ((my_flush_threshold_dumped < tapeq_size &&
-                    my_flush_threshold_scheduled < sched_size) ||
+                  (allow_size_or_number ||
+                   (data_lost > data_lost_next_tape) ||
                    nb_taper_active == 0) &&
                   (last_started_taper == NULL ||
                    last_started_taper == taper)) {
@@ -3780,43 +3869,44 @@ tape_action(
         !empty(directq) ||                             // if a dle is waiting for a dump to tape
          !empty(roomq) ||                              // holding disk constraint
          idle_reason == IDLE_NO_DISKSPACE ||           // holding disk constraint
-         (my_flush_threshold_dumped < tapeq_size &&    // flush-threshold-dumped &&
-         my_flush_threshold_scheduled < sched_size) || //  flush-threshold-scheduled
-        (my_taperflush < tapeq_size &&                 // taperflush
+        allow_size_or_number ||
+        (data_lost > data_lost_next_tape) ||
+        (taperflush < tapeq_size &&                    // taperflush
          (force_flush == 1 ||                          //  if force_flush
           dump_to_disk_terminated))                    //  or all dump to disk terminated
        )) {
+       driver_debug(2, "tape_action: TAPER_STATE_WAIT_FOR_TAPE return TAPE_ACTION_NEW_TAPE\n");
        result |= TAPE_ACTION_NEW_TAPE;
     // when to stop using new tape
     } else if ((taper->state & TAPER_STATE_WAIT_FOR_TAPE) &&
-              (my_taperflush >= tapeq_size &&          // taperflush criteria
+              (taperflush >= tapeq_size &&             // taperflush criteria
               (force_flush == 1 ||                     //  if force_flush
                dump_to_disk_terminated))               //  or all dump to disk
              ) {
+       driver_debug(2, "tape_action: TAPER_STATE_WAIT_FOR_TAPE B\n");
        if (nb_taper_active <= 0) {
            if (current_tape >= conf_runtapes) {
                *why_no_new_tape = g_strdup_printf(_("%d tapes filled; runtapes=%d "
                      "does not allow additional tapes"), current_tape, conf_runtapes);
-           } else {
+               driver_debug(2, "tape_action: TAPER_STATE_WAIT_FOR_TAPE return TAPE_ACTION_NO_NEW_TAPE\n");
+               result |= TAPE_ACTION_NO_NEW_TAPE;
+           } else if (dumpers_size <= 0) {
                *why_no_new_tape = _("taperflush criteria not met");
+               driver_debug(2, "tape_action: TAPER_STATE_WAIT_FOR_TAPE return TAPE_ACTION_NO_NEW_TAPE\n");
+               result |= TAPE_ACTION_NO_NEW_TAPE;
            }
-           result |= TAPE_ACTION_NO_NEW_TAPE;
        }
     }
 
     // when to start a flush
-    // We don't start a flush if taper_tape_started == 1 && dump_to_disk_terminated && force_flush == 0,
-    // it is a criteria need to exit the first event_loop without flushing everything to tape,
-    // they will be flush in another event_loop.
     if (taper->state & TAPER_STATE_IDLE) {
+       driver_debug(2, "tape_action: TAPER_STATE_IDLE\n");
        if (!degraded_mode && (!empty(tapeq) || !empty(directq)) &&
-           (((taper->state & TAPER_STATE_TAPE_STARTED) &&
-             force_flush == 1) ||                              // if tape already started and force_flush
+           (taper->state & TAPER_STATE_TAPE_STARTED ||         // tape already started 
              !empty(roomq) ||                                  // holding disk constraint
              idle_reason == IDLE_NO_DISKSPACE ||               // holding disk constraint
-             (my_flush_threshold_dumped < tapeq_size &&                // flush-threshold-dumped &&
-             my_flush_threshold_scheduled < sched_size) ||     //  flush-threshold-scheduled
-             (force_flush == 1 && my_taperflush < tapeq_size))) {      // taperflush if force_flush
+            allow_size_or_number ||
+             (force_flush == 1 && taperflush < tapeq_size))) { // taperflush if force_flush
 
            if (nb_taper_flushing == 0) {
                result |= TAPE_ACTION_START_A_FLUSH;
@@ -3829,14 +3919,13 @@ tape_action(
 }
 
 static int
-all_taper_idle(void)
+no_taper_flushing(void)
 {
     taper_t *taper;
 
     for (taper = tapetable; taper < tapetable + conf_taper_parallel_write;
         taper++) {
-       if (taper->state & TAPER_STATE_DUMP_TO_TAPE ||
-           taper->state & TAPER_STATE_FILE_TO_TAPE)
+       if (taper->state & TAPER_STATE_FILE_TO_TAPE)
            return 0;
     }
     return 1;