]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/spool.c
kes Add DataDespooling and DataCommitting status (committing is
[bacula/bacula] / bacula / src / stored / spool.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2004-2007 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *  Spooling code
30  *
31  *      Kern Sibbald, March 2004
32  *
33  *  Version $Id$
34  */
35
36 #include "bacula.h"
37 #include "stored.h"
38
39 /* Forward referenced subroutines */
40 static void make_unique_data_spool_filename(DCR *dcr, POOLMEM **name);
41 static bool open_data_spool_file(DCR *dcr);
42 static bool close_data_spool_file(DCR *dcr);
43 static bool despool_data(DCR *dcr, bool commit);
44 static int  read_block_from_spool_file(DCR *dcr);
45 static bool open_attr_spool_file(JCR *jcr, BSOCK *bs);
46 static bool close_attr_spool_file(JCR *jcr, BSOCK *bs);
47 static bool write_spool_header(DCR *dcr);
48 static bool write_spool_data(DCR *dcr);
49
50 struct spool_stats_t {
51    uint32_t data_jobs;                /* current jobs spooling data */
52    uint32_t attr_jobs;
53    uint32_t total_data_jobs;          /* total jobs to have spooled data */
54    uint32_t total_attr_jobs;
55    int64_t max_data_size;             /* max data size */
56    int64_t max_attr_size;
57    int64_t data_size;                 /* current data size (all jobs running) */
58    int64_t attr_size;
59 };
60
61 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
62 spool_stats_t spool_stats;
63
64 /*
65  * Header for data spool record */
66 struct spool_hdr {
67    int32_t  FirstIndex;               /* FirstIndex for buffer */
68    int32_t  LastIndex;                /* LastIndex for buffer */
69    uint32_t len;                      /* length of next buffer */
70 };
71
72 enum {
73    RB_EOT = 1,
74    RB_ERROR,
75    RB_OK
76 };
77
78 void list_spool_stats(void sendit(const char *msg, int len, void *sarg), void *arg)
79 {
80    char ed1[30], ed2[30];
81    POOL_MEM msg(PM_MESSAGE);
82    int len;
83
84    len = Mmsg(msg, _("Spooling statistics:\n"));
85
86    if (spool_stats.data_jobs || spool_stats.max_data_size) {
87       len = Mmsg(msg, _("Data spooling: %u active jobs, %s bytes; %u total jobs, %s max bytes/job.\n"),
88          spool_stats.data_jobs, edit_uint64_with_commas(spool_stats.data_size, ed1),
89          spool_stats.total_data_jobs,
90          edit_uint64_with_commas(spool_stats.max_data_size, ed2));
91
92       sendit(msg.c_str(), len, arg);
93    }
94    if (spool_stats.attr_jobs || spool_stats.max_attr_size) {
95       len = Mmsg(msg, _("Attr spooling: %u active jobs, %s bytes; %u total jobs, %s max bytes.\n"),
96          spool_stats.attr_jobs, edit_uint64_with_commas(spool_stats.attr_size, ed1),
97          spool_stats.total_attr_jobs,
98          edit_uint64_with_commas(spool_stats.max_attr_size, ed2));
99    
100       sendit(msg.c_str(), len, arg);
101    }
102    len = Mmsg(msg, "====\n");
103    sendit(msg.c_str(), len, arg);
104 }
105
106 bool begin_data_spool(DCR *dcr)
107 {
108    bool stat = true;
109    if (!dcr->dev->is_dvd() && dcr->jcr->spool_data) {
110       Dmsg0(100, "Turning on data spooling\n");
111       dcr->spool_data = true;
112       stat = open_data_spool_file(dcr);
113       if (stat) {
114          dcr->spooling = true;
115          Jmsg(dcr->jcr, M_INFO, 0, _("Spooling data ...\n"));
116          P(mutex);
117          spool_stats.data_jobs++;
118          V(mutex);
119       }
120    }
121    return stat;
122 }
123
124 bool discard_data_spool(DCR *dcr)
125 {
126    if (dcr->spooling) {
127       Dmsg0(100, "Data spooling discarded\n");
128       return close_data_spool_file(dcr);
129    }
130    return true;
131 }
132
133 bool commit_data_spool(DCR *dcr)
134 {
135    bool stat;
136
137    if (dcr->spooling) {
138       Dmsg0(100, "Committing spooled data\n");
139       stat = despool_data(dcr, true /*commit*/);
140       if (!stat) {
141          Dmsg1(100, _("Bad return from despool WroteVol=%d\n"), dcr->WroteVol);
142          close_data_spool_file(dcr);
143          return false;
144       }
145       return close_data_spool_file(dcr);
146    }
147    return true;
148 }
149
150 static void make_unique_data_spool_filename(DCR *dcr, POOLMEM **name)
151 {
152    const char *dir;
153    if (dcr->dev->device->spool_directory) {
154       dir = dcr->dev->device->spool_directory;
155    } else {
156       dir = working_directory;
157    }
158    Mmsg(name, "%s/%s.data.%u.%s.%s.spool", dir, my_name, dcr->jcr->JobId,
159         dcr->jcr->Job, dcr->device->hdr.name);
160 }
161
162
163 static bool open_data_spool_file(DCR *dcr)
164 {
165    POOLMEM *name  = get_pool_memory(PM_MESSAGE);
166    int spool_fd;
167
168    make_unique_data_spool_filename(dcr, &name);
169    if ((spool_fd = open(name, O_CREAT|O_TRUNC|O_RDWR|O_BINARY, 0640)) >= 0) {
170       dcr->spool_fd = spool_fd;
171       dcr->jcr->spool_attributes = true;
172    } else {
173       berrno be;
174       Jmsg(dcr->jcr, M_FATAL, 0, _("Open data spool file %s failed: ERR=%s\n"), name,
175            be.bstrerror());
176       free_pool_memory(name);
177       return false;
178    }
179    Dmsg1(100, "Created spool file: %s\n", name);
180    free_pool_memory(name);
181    return true;
182 }
183
184 static bool close_data_spool_file(DCR *dcr)
185 {
186    POOLMEM *name  = get_pool_memory(PM_MESSAGE);
187
188    P(mutex);
189    spool_stats.data_jobs--;
190    spool_stats.total_data_jobs++;
191    if (spool_stats.data_size < dcr->job_spool_size) {
192       spool_stats.data_size = 0;
193    } else {
194       spool_stats.data_size -= dcr->job_spool_size;
195    }
196    dcr->job_spool_size = 0;
197    V(mutex);
198
199    make_unique_data_spool_filename(dcr, &name);
200    close(dcr->spool_fd);
201    dcr->spool_fd = -1;
202    dcr->spooling = false;
203    unlink(name);
204    Dmsg1(100, "Deleted spool file: %s\n", name);
205    free_pool_memory(name);
206    return true;
207 }
208
209 static const char *spool_name = "*spool*";
210
211 /*
212  * NB! This routine locks the device, but if committing will
213  *     not unlock it. If not committing, it will be unlocked.
214  */
215 static bool despool_data(DCR *dcr, bool commit)
216 {
217    DEVICE *rdev;
218    DCR *rdcr;
219    bool ok = true;
220    DEV_BLOCK *block;
221    JCR *jcr = dcr->jcr;
222    int stat;
223    char ec1[50];
224
225    Dmsg0(100, "Despooling data\n");
226    /*
227     * Commit means that the job is done, so we commit, otherwise, we
228     *  are despooling because of user spool size max or some error  
229     *  (e.g. filesystem full).
230     */
231    if (commit) {
232       Jmsg(jcr, M_INFO, 0, _("Committing spooled data to Volume \"%s\". Despooling %s bytes ...\n"),
233          jcr->dcr->VolumeName,
234          edit_uint64_with_commas(jcr->dcr->job_spool_size, ec1));
235       set_jcr_job_status(jcr, JS_DataCommitting);
236    } else {
237       Jmsg(jcr, M_INFO, 0, _("Writing spooled data to Volume. Despooling %s bytes ...\n"),
238          edit_uint64_with_commas(jcr->dcr->job_spool_size, ec1));
239       set_jcr_job_status(jcr, JS_DataDespooling);
240    }
241    set_jcr_job_status(jcr, JS_DataDespooling);
242    dir_send_job_status(jcr);
243    dcr->despool_wait = true;
244    dcr->spooling = false;
245    /*
246     * We work with device blocked, but not locked so that
247     *  other threads -- e.g. reservations can lock the device
248     *  structure.
249     */
250    dcr->dblock(BST_DESPOOLING);
251    dcr->despool_wait = false;
252    dcr->despooling = true;
253
254    /*
255     * This is really quite kludgy and should be fixed some time.
256     * We create a dev structure to read from the spool file
257     * in rdev and rdcr.
258     */
259    rdev = (DEVICE *)malloc(sizeof(DEVICE));
260    memset(rdev, 0, sizeof(DEVICE));
261    rdev->dev_name = get_memory(strlen(spool_name)+1);
262    bstrncpy(rdev->dev_name, spool_name, sizeof(rdev->dev_name));
263    rdev->errmsg = get_pool_memory(PM_EMSG);
264    *rdev->errmsg = 0;
265    rdev->max_block_size = dcr->dev->max_block_size;
266    rdev->min_block_size = dcr->dev->min_block_size;
267    rdev->device = dcr->dev->device;
268    rdcr = new_dcr(jcr, NULL, rdev);
269    rdcr->spool_fd = dcr->spool_fd;
270    block = dcr->block;                /* save block */
271    dcr->block = rdcr->block;          /* make read and write block the same */
272
273    Dmsg1(800, "read/write block size = %d\n", block->buf_len);
274    lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */
275
276 #if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED)
277    posix_fadvise(rdcr->spool_fd, 0, 0, POSIX_FADV_WILLNEED);
278 #endif
279
280    /* Add run time, to get current wait time */
281    time_t despool_start = time(NULL) - jcr->run_time;
282
283    for ( ; ok; ) {
284       if (job_canceled(jcr)) {
285          ok = false;
286          break;
287       }
288       stat = read_block_from_spool_file(rdcr);
289       if (stat == RB_EOT) {
290          break;
291       } else if (stat == RB_ERROR) {
292          ok = false;
293          break;
294       }
295       ok = write_block_to_device(dcr);
296       if (!ok) {
297          Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
298                dcr->dev->print_name(), dcr->dev->bstrerror());
299       }
300       Dmsg3(800, "Write block ok=%d FI=%d LI=%d\n", ok, block->FirstIndex, block->LastIndex);
301    }
302
303    /* Subtracting run_time give us elapsed time - wait_time since we started despooling */
304    time_t despool_elapsed = time(NULL) - despool_start - jcr->run_time;
305
306    if (despool_elapsed <= 0) {
307       despool_elapsed = 1;
308    }
309
310    Jmsg(dcr->jcr, M_INFO, 0, _("Despooling elapsed time = %02d:%02d:%02d, Transfer rate = %s bytes/second\n"),
311          despool_elapsed / 3600, despool_elapsed % 3600 / 60, despool_elapsed % 60,
312          edit_uint64_with_suffix(jcr->dcr->job_spool_size / despool_elapsed, ec1));
313
314    dcr->block = block;                /* reset block */
315
316    lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */
317    if (ftruncate(rdcr->spool_fd, 0) != 0) {
318       berrno be;
319       Jmsg(dcr->jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
320          be.bstrerror());
321       /* Note, try continuing despite ftruncate problem */
322    }
323
324    P(mutex);
325    if (spool_stats.data_size < dcr->job_spool_size) {
326       spool_stats.data_size = 0;
327    } else {
328       spool_stats.data_size -= dcr->job_spool_size;
329    }
330    V(mutex);
331    P(dcr->dev->spool_mutex);
332    dcr->dev->spool_size -= dcr->job_spool_size;
333    dcr->job_spool_size = 0;            /* zap size in input dcr */
334    V(dcr->dev->spool_mutex);
335    free_memory(rdev->dev_name);
336    free_pool_memory(rdev->errmsg);
337    /* Be careful to NULL the jcr and free rdev after free_dcr() */
338    rdcr->jcr = NULL;
339    rdcr->dev = NULL;
340    free_dcr(rdcr);
341    free(rdev);
342    dcr->spooling = true;           /* turn on spooling again */
343    dcr->despooling = false;
344
345    /* 
346     * We are done, so unblock the device, but if we have done a 
347     *  commit, leave it locked so that the job cleanup does not
348     *  need to wait to release the device (no re-acquire of the lock).
349     */
350    dcr->dlock();
351    unblock_device(dcr->dev);
352    /* If doing a commit, leave the device locked -- unlocked in release_device() */
353    if (!commit) {
354       dcr->dunlock();
355    }
356    set_jcr_job_status(jcr, JS_Running);
357    dir_send_job_status(jcr);
358    return ok;
359 }
360
361 /*
362  * Read a block from the spool file
363  *
364  *  Returns RB_OK on success
365  *          RB_EOT when file done
366  *          RB_ERROR on error
367  */
368 static int read_block_from_spool_file(DCR *dcr)
369 {
370    uint32_t rlen;
371    ssize_t stat;
372    spool_hdr hdr;
373    DEV_BLOCK *block = dcr->block;
374
375    rlen = sizeof(hdr);
376    stat = read(dcr->spool_fd, (char *)&hdr, (size_t)rlen);
377    if (stat == 0) {
378       Dmsg0(100, "EOT on spool read.\n");
379       return RB_EOT;
380    } else if (stat != (ssize_t)rlen) {
381       if (stat == -1) {
382          berrno be;
383          Jmsg(dcr->jcr, M_FATAL, 0, _("Spool header read error. ERR=%s\n"),
384               be.bstrerror());
385       } else {
386          Pmsg2(000, _("Spool read error. Wanted %u bytes, got %d\n"), rlen, stat);
387          Jmsg2(dcr->jcr, M_FATAL, 0, _("Spool header read error. Wanted %u bytes, got %d\n"), rlen, stat);
388       }
389       return RB_ERROR;
390    }
391    rlen = hdr.len;
392    if (rlen > block->buf_len) {
393       Pmsg2(000, _("Spool block too big. Max %u bytes, got %u\n"), block->buf_len, rlen);
394       Jmsg2(dcr->jcr, M_FATAL, 0, _("Spool block too big. Max %u bytes, got %u\n"), block->buf_len, rlen);
395       return RB_ERROR;
396    }
397    stat = read(dcr->spool_fd, (char *)block->buf, (size_t)rlen);
398    if (stat != (ssize_t)rlen) {
399       Pmsg2(000, _("Spool data read error. Wanted %u bytes, got %d\n"), rlen, stat);
400       Jmsg2(dcr->jcr, M_FATAL, 0, _("Spool data read error. Wanted %u bytes, got %d\n"), rlen, stat);
401       return RB_ERROR;
402    }
403    /* Setup write pointers */
404    block->binbuf = rlen;
405    block->bufp = block->buf + block->binbuf;
406    block->FirstIndex = hdr.FirstIndex;
407    block->LastIndex = hdr.LastIndex;
408    block->VolSessionId = dcr->jcr->VolSessionId;
409    block->VolSessionTime = dcr->jcr->VolSessionTime;
410    Dmsg2(800, "Read block FI=%d LI=%d\n", block->FirstIndex, block->LastIndex);
411    return RB_OK;
412 }
413
414 /*
415  * Write a block to the spool file
416  *
417  *  Returns: true on success or EOT
418  *           false on hard error
419  */
420 bool write_block_to_spool_file(DCR *dcr)
421 {
422    uint32_t wlen, hlen;               /* length to write */
423    bool despool = false;
424    DEV_BLOCK *block = dcr->block;
425
426    ASSERT(block->binbuf == ((uint32_t) (block->bufp - block->buf)));
427    if (block->binbuf <= WRITE_BLKHDR_LENGTH) {  /* Does block have data in it? */
428       return true;
429    }
430
431    hlen = sizeof(spool_hdr);
432    wlen = block->binbuf;
433    P(dcr->dev->spool_mutex);
434    dcr->job_spool_size += hlen + wlen;
435    dcr->dev->spool_size += hlen + wlen;
436    if ((dcr->max_job_spool_size > 0 && dcr->job_spool_size >= dcr->max_job_spool_size) ||
437        (dcr->dev->max_spool_size > 0 && dcr->dev->spool_size >= dcr->dev->max_spool_size)) {
438       despool = true;
439    }
440    V(dcr->dev->spool_mutex);
441    P(mutex);
442    spool_stats.data_size += hlen + wlen;
443    if (spool_stats.data_size > spool_stats.max_data_size) {
444       spool_stats.max_data_size = spool_stats.data_size;
445    }
446    V(mutex);
447    if (despool) {
448 #ifdef xDEBUG
449       char ec1[30], ec2[30], ec3[30], ec4[30];
450       Dmsg4(100, "Despool in write_block_to_spool_file max_size=%s size=%s "
451             "max_job_size=%s job_size=%s\n",
452             edit_uint64_with_commas(dcr->max_job_spool_size, ec1),
453             edit_uint64_with_commas(dcr->job_spool_size, ec2),
454             edit_uint64_with_commas(dcr->dev->max_spool_size, ec3),
455             edit_uint64_with_commas(dcr->dev->spool_size, ec4));
456 #endif
457       Jmsg(dcr->jcr, M_INFO, 0, _("User specified spool size reached.\n"));
458       if (!despool_data(dcr, false)) {
459          Pmsg0(000, _("Bad return from despool in write_block.\n"));
460          return false;
461       }
462       /* Despooling cleared these variables so reset them */
463       P(dcr->dev->spool_mutex);
464       dcr->job_spool_size += hlen + wlen;
465       dcr->dev->spool_size += hlen + wlen;
466       V(dcr->dev->spool_mutex);
467       Jmsg(dcr->jcr, M_INFO, 0, _("Spooling data again ...\n"));
468    }
469
470
471    if (!write_spool_header(dcr)) {
472       return false;
473    }
474    if (!write_spool_data(dcr)) {
475      return false;
476    }
477
478    Dmsg2(800, "Wrote block FI=%d LI=%d\n", block->FirstIndex, block->LastIndex);
479    empty_block(block);
480    return true;
481 }
482
483 static bool write_spool_header(DCR *dcr)
484 {
485    spool_hdr hdr;
486    ssize_t stat;
487    DEV_BLOCK *block = dcr->block;
488
489    hdr.FirstIndex = block->FirstIndex;
490    hdr.LastIndex = block->LastIndex;
491    hdr.len = block->binbuf;
492
493    /* Write header */
494    for (int retry=0; retry<=1; retry++) {
495       stat = write(dcr->spool_fd, (char*)&hdr, sizeof(hdr));
496       if (stat == -1) {
497          berrno be;
498          Jmsg(dcr->jcr, M_FATAL, 0, _("Error writing header to spool file. ERR=%s\n"),
499               be.bstrerror());
500       }
501       if (stat != (ssize_t)sizeof(hdr)) {
502          /* If we wrote something, truncate it, then despool */
503          if (stat != -1) {
504 #if defined(HAVE_WIN32)
505             boffset_t   pos = _lseeki64(dcr->spool_fd, (__int64)0, SEEK_CUR);
506 #else
507             boffset_t   pos = lseek(dcr->spool_fd, (off_t)0, SEEK_CUR);
508 #endif
509             if (ftruncate(dcr->spool_fd, pos - stat) != 0) {
510                berrno be;
511                Jmsg(dcr->jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
512                   be.bstrerror());
513               /* Note, try continuing despite ftruncate problem */
514             }
515          }
516          if (!despool_data(dcr, false)) {
517             Jmsg(dcr->jcr, M_FATAL, 0, _("Fatal despooling error."));
518             return false;
519          }
520          continue;                    /* try again */
521       }
522       return true;
523    }
524    Jmsg(dcr->jcr, M_FATAL, 0, _("Retrying after header spooling error failed.\n"));
525    return false;
526 }
527
528 static bool write_spool_data(DCR *dcr)
529 {
530    ssize_t stat;
531    DEV_BLOCK *block = dcr->block;
532
533    /* Write data */
534    for (int retry=0; retry<=1; retry++) {
535       stat = write(dcr->spool_fd, block->buf, (size_t)block->binbuf);
536       if (stat == -1) {
537          berrno be;
538          Jmsg(dcr->jcr, M_FATAL, 0, _("Error writing data to spool file. ERR=%s\n"),
539               be.bstrerror());
540       }
541       if (stat != (ssize_t)block->binbuf) {
542          /*
543           * If we wrote something, truncate it and the header, then despool
544           */
545          if (stat != -1) {
546 #if defined(HAVE_WIN32)
547             boffset_t   pos = _lseeki64(dcr->spool_fd, (__int64)0, SEEK_CUR);
548 #else
549             boffset_t   pos = lseek(dcr->spool_fd, (off_t)0, SEEK_CUR);
550 #endif
551             if (ftruncate(dcr->spool_fd, pos - stat - sizeof(spool_hdr)) != 0) {
552                berrno be;
553                Jmsg(dcr->jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
554                   be.bstrerror());
555                /* Note, try continuing despite ftruncate problem */
556             }
557          }
558          if (!despool_data(dcr, false)) {
559             Jmsg(dcr->jcr, M_FATAL, 0, _("Fatal despooling error."));
560             return false;
561          }
562          if (!write_spool_header(dcr)) {
563             return false;
564          }
565          continue;                    /* try again */
566       }
567       return true;
568    }
569    Jmsg(dcr->jcr, M_FATAL, 0, _("Retrying after data spooling error failed.\n"));
570    return false;
571 }
572
573
574
575 bool are_attributes_spooled(JCR *jcr)
576 {
577    return jcr->spool_attributes && jcr->dir_bsock->m_spool_fd;
578 }
579
580 /*
581  * Create spool file for attributes.
582  *  This is done by "attaching" to the bsock, and when
583  *  it is called, the output is written to a file.
584  *  The actual spooling is turned on and off in
585  *  append.c only during writing of the attributes.
586  */
587 bool begin_attribute_spool(JCR *jcr)
588 {
589    if (!jcr->no_attributes && jcr->spool_attributes) {
590       return open_attr_spool_file(jcr, jcr->dir_bsock);
591    }
592    return true;
593 }
594
595 bool discard_attribute_spool(JCR *jcr)
596 {
597    if (are_attributes_spooled(jcr)) {
598       return close_attr_spool_file(jcr, jcr->dir_bsock);
599    }
600    return true;
601 }
602
603 static void update_attr_spool_size(ssize_t size)
604 {
605    P(mutex);
606    if (size > 0) {
607      if ((spool_stats.attr_size - size) > 0) {
608         spool_stats.attr_size -= size;
609      } else {
610         spool_stats.attr_size = 0;
611      }
612    }
613    V(mutex);
614 }
615
616 bool commit_attribute_spool(JCR *jcr)
617 {
618    off_t size;
619    char ec1[30];
620
621    if (are_attributes_spooled(jcr)) {
622       if (fseeko(jcr->dir_bsock->m_spool_fd, 0, SEEK_END) != 0) {
623          berrno be;
624          Jmsg(jcr, M_FATAL, 0, _("Fseek on attributes file failed: ERR=%s\n"),
625               be.bstrerror());
626          goto bail_out;
627       }
628       size = ftello(jcr->dir_bsock->m_spool_fd);
629       if (size < 0) {
630          berrno be;
631          Jmsg(jcr, M_FATAL, 0, _("Fseek on attributes file failed: ERR=%s\n"),
632               be.bstrerror());
633          goto bail_out;
634       }
635       P(mutex);
636       if (spool_stats.attr_size + size > spool_stats.max_attr_size) {
637          spool_stats.max_attr_size = spool_stats.attr_size + size;
638       }
639       spool_stats.attr_size += size;
640       V(mutex);
641       set_jcr_job_status(jcr, JS_AttrDespooling);
642       dir_send_job_status(jcr);
643       Jmsg(jcr, M_INFO, 0, _("Sending spooled attrs to the Director. Despooling %s bytes ...\n"),
644             edit_uint64_with_commas(size, ec1));
645       jcr->dir_bsock->despool(update_attr_spool_size, size);
646       return close_attr_spool_file(jcr, jcr->dir_bsock);
647    }
648    return true;
649
650 bail_out:
651    close_attr_spool_file(jcr, jcr->dir_bsock);
652    return false;
653 }
654
655 static void make_unique_spool_filename(JCR *jcr, POOLMEM **name, int fd)
656 {
657    Mmsg(name, "%s/%s.attr.%s.%d.spool", working_directory, my_name,
658       jcr->Job, fd);
659 }
660
661
662 bool open_attr_spool_file(JCR *jcr, BSOCK *bs)
663 {
664    POOLMEM *name  = get_pool_memory(PM_MESSAGE);
665
666    make_unique_spool_filename(jcr, &name, bs->m_fd);
667    bs->m_spool_fd = fopen(name, "w+b");
668    if (!bs->m_spool_fd) {
669       berrno be;
670       Jmsg(jcr, M_FATAL, 0, _("fopen attr spool file %s failed: ERR=%s\n"), name,
671            be.bstrerror());
672       free_pool_memory(name);
673       return false;
674    }
675    P(mutex);
676    spool_stats.attr_jobs++;
677    V(mutex);
678    free_pool_memory(name);
679    return true;
680 }
681
682 bool close_attr_spool_file(JCR *jcr, BSOCK *bs)
683 {
684    POOLMEM *name;
685
686    if (!bs->m_spool_fd) {
687       return true;
688    }
689    name = get_pool_memory(PM_MESSAGE);
690    P(mutex);
691    spool_stats.attr_jobs--;
692    spool_stats.total_attr_jobs++;
693    V(mutex);
694    make_unique_spool_filename(jcr, &name, bs->m_fd);
695    fclose(bs->m_spool_fd);
696    unlink(name);
697    free_pool_memory(name);
698    bs->m_spool_fd = NULL;
699    bs->clear_spooling();
700    return true;
701 }