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