]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/spool.c
kes Attempt to correct problems with restores with autochangers and
[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 }
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
252    /*
253     * This is really quite kludgy and should be fixed some time.
254     * We create a dev structure to read from the spool file
255     * in rdev and rdcr.
256     */
257    rdev = (DEVICE *)malloc(sizeof(DEVICE));
258    memset(rdev, 0, sizeof(DEVICE));
259    rdev->dev_name = get_memory(strlen(spool_name)+1);
260    bstrncpy(rdev->dev_name, spool_name, sizeof(rdev->dev_name));
261    rdev->errmsg = get_pool_memory(PM_EMSG);
262    *rdev->errmsg = 0;
263    rdev->max_block_size = dcr->dev->max_block_size;
264    rdev->min_block_size = dcr->dev->min_block_size;
265    rdev->device = dcr->dev->device;
266    rdcr = new_dcr(jcr, NULL, rdev);
267    rdcr->spool_fd = dcr->spool_fd;
268    block = dcr->block;                /* save block */
269    dcr->block = rdcr->block;          /* make read and write block the same */
270
271    Dmsg1(800, "read/write block size = %d\n", block->buf_len);
272    lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */
273
274 #if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED)
275    posix_fadvise(rdcr->spool_fd, 0, 0, POSIX_FADV_WILLNEED);
276 #endif
277
278    /* Add run time, to get current wait time */
279    time_t despool_start = time(NULL) - jcr->run_time;
280
281    for ( ; ok; ) {
282       if (job_canceled(jcr)) {
283          ok = false;
284          break;
285       }
286       stat = read_block_from_spool_file(rdcr);
287       if (stat == RB_EOT) {
288          break;
289       } else if (stat == RB_ERROR) {
290          ok = false;
291          break;
292       }
293       ok = write_block_to_device(dcr);
294       if (!ok) {
295          Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
296                dcr->dev->print_name(), dcr->dev->bstrerror());
297       }
298       Dmsg3(800, "Write block ok=%d FI=%d LI=%d\n", ok, block->FirstIndex, block->LastIndex);
299    }
300
301    /* Subtracting run_time give us elapsed time - wait_time since we started despooling */
302    time_t despool_elapsed = time(NULL) - despool_start - jcr->run_time;
303
304    if (despool_elapsed <= 0) {
305       despool_elapsed = 1;
306    }
307
308    Jmsg(dcr->jcr, M_INFO, 0, _("Despooling elapsed time = %02d:%02d:%02d, Transfer rate = %s bytes/second\n"),
309          despool_elapsed / 3600, despool_elapsed % 3600 / 60, despool_elapsed % 60,
310          edit_uint64_with_suffix(jcr->dcr->job_spool_size / despool_elapsed, ec1));
311
312    dcr->block = block;                /* reset block */
313
314    lseek(rdcr->spool_fd, 0, SEEK_SET); /* rewind */
315    if (ftruncate(rdcr->spool_fd, 0) != 0) {
316       berrno be;
317       Jmsg(dcr->jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
318          be.bstrerror());
319       /* Note, try continuing despite ftruncate problem */
320    }
321
322    P(mutex);
323    if (spool_stats.data_size < dcr->job_spool_size) {
324       spool_stats.data_size = 0;
325    } else {
326       spool_stats.data_size -= dcr->job_spool_size;
327    }
328    V(mutex);
329    P(dcr->dev->spool_mutex);
330    dcr->dev->spool_size -= dcr->job_spool_size;
331    dcr->job_spool_size = 0;            /* zap size in input dcr */
332    V(dcr->dev->spool_mutex);
333    free_memory(rdev->dev_name);
334    free_pool_memory(rdev->errmsg);
335    /* Be careful to NULL the jcr and free rdev after free_dcr() */
336    rdcr->jcr = NULL;
337    rdcr->dev = NULL;
338    free_dcr(rdcr);
339    free(rdev);
340    dcr->spooling = true;           /* turn on spooling again */
341    dcr->despooling = false;
342
343    /* 
344     * We are done, so unblock the device, but if we have done a 
345     *  commit, leave it locked so that the job cleanup does not
346     *  need to wait to release the device (no re-acquire of the lock).
347     */
348    dcr->dlock();
349    unblock_device(dcr->dev);
350    /* If doing a commit, leave the device locked -- unlocked in release_device() */
351    if (!commit) {
352       dcr->dunlock();
353    }
354    set_jcr_job_status(jcr, JS_Running);
355    dir_send_job_status(jcr);
356    return ok;
357 }
358
359 /*
360  * Read a block from the spool file
361  *
362  *  Returns RB_OK on success
363  *          RB_EOT when file done
364  *          RB_ERROR on error
365  */
366 static int read_block_from_spool_file(DCR *dcr)
367 {
368    uint32_t rlen;
369    ssize_t stat;
370    spool_hdr hdr;
371    DEV_BLOCK *block = dcr->block;
372
373    rlen = sizeof(hdr);
374    stat = read(dcr->spool_fd, (char *)&hdr, (size_t)rlen);
375    if (stat == 0) {
376       Dmsg0(100, "EOT on spool read.\n");
377       return RB_EOT;
378    } else if (stat != (ssize_t)rlen) {
379       if (stat == -1) {
380          berrno be;
381          Jmsg(dcr->jcr, M_FATAL, 0, _("Spool header read error. ERR=%s\n"),
382               be.bstrerror());
383       } else {
384          Pmsg2(000, _("Spool read error. Wanted %u bytes, got %d\n"), rlen, stat);
385          Jmsg2(dcr->jcr, M_FATAL, 0, _("Spool header read error. Wanted %u bytes, got %d\n"), rlen, stat);
386       }
387       return RB_ERROR;
388    }
389    rlen = hdr.len;
390    if (rlen > block->buf_len) {
391       Pmsg2(000, _("Spool block too big. Max %u bytes, got %u\n"), block->buf_len, rlen);
392       Jmsg2(dcr->jcr, M_FATAL, 0, _("Spool block too big. Max %u bytes, got %u\n"), block->buf_len, rlen);
393       return RB_ERROR;
394    }
395    stat = read(dcr->spool_fd, (char *)block->buf, (size_t)rlen);
396    if (stat != (ssize_t)rlen) {
397       Pmsg2(000, _("Spool data read error. Wanted %u bytes, got %d\n"), rlen, stat);
398       Jmsg2(dcr->jcr, M_FATAL, 0, _("Spool data read error. Wanted %u bytes, got %d\n"), rlen, stat);
399       return RB_ERROR;
400    }
401    /* Setup write pointers */
402    block->binbuf = rlen;
403    block->bufp = block->buf + block->binbuf;
404    block->FirstIndex = hdr.FirstIndex;
405    block->LastIndex = hdr.LastIndex;
406    block->VolSessionId = dcr->jcr->VolSessionId;
407    block->VolSessionTime = dcr->jcr->VolSessionTime;
408    Dmsg2(800, "Read block FI=%d LI=%d\n", block->FirstIndex, block->LastIndex);
409    return RB_OK;
410 }
411
412 /*
413  * Write a block to the spool file
414  *
415  *  Returns: true on success or EOT
416  *           false on hard error
417  */
418 bool write_block_to_spool_file(DCR *dcr)
419 {
420    uint32_t wlen, hlen;               /* length to write */
421    bool despool = false;
422    DEV_BLOCK *block = dcr->block;
423
424    ASSERT(block->binbuf == ((uint32_t) (block->bufp - block->buf)));
425    if (block->binbuf <= WRITE_BLKHDR_LENGTH) {  /* Does block have data in it? */
426       return true;
427    }
428
429    hlen = sizeof(spool_hdr);
430    wlen = block->binbuf;
431    P(dcr->dev->spool_mutex);
432    dcr->job_spool_size += hlen + wlen;
433    dcr->dev->spool_size += hlen + wlen;
434    if ((dcr->max_job_spool_size > 0 && dcr->job_spool_size >= dcr->max_job_spool_size) ||
435        (dcr->dev->max_spool_size > 0 && dcr->dev->spool_size >= dcr->dev->max_spool_size)) {
436       despool = true;
437    }
438    V(dcr->dev->spool_mutex);
439    P(mutex);
440    spool_stats.data_size += hlen + wlen;
441    if (spool_stats.data_size > spool_stats.max_data_size) {
442       spool_stats.max_data_size = spool_stats.data_size;
443    }
444    V(mutex);
445    if (despool) {
446 #ifdef xDEBUG
447       char ec1[30], ec2[30], ec3[30], ec4[30];
448       Dmsg4(100, "Despool in write_block_to_spool_file max_size=%s size=%s "
449             "max_job_size=%s job_size=%s\n",
450             edit_uint64_with_commas(dcr->max_job_spool_size, ec1),
451             edit_uint64_with_commas(dcr->job_spool_size, ec2),
452             edit_uint64_with_commas(dcr->dev->max_spool_size, ec3),
453             edit_uint64_with_commas(dcr->dev->spool_size, ec4));
454 #endif
455       Jmsg(dcr->jcr, M_INFO, 0, _("User specified spool size reached.\n"));
456       if (!despool_data(dcr, false)) {
457          Pmsg0(000, _("Bad return from despool in write_block.\n"));
458          return false;
459       }
460       /* Despooling cleared these variables so reset them */
461       P(dcr->dev->spool_mutex);
462       dcr->job_spool_size += hlen + wlen;
463       dcr->dev->spool_size += hlen + wlen;
464       V(dcr->dev->spool_mutex);
465       Jmsg(dcr->jcr, M_INFO, 0, _("Spooling data again ...\n"));
466    }
467
468
469    if (!write_spool_header(dcr)) {
470       return false;
471    }
472    if (!write_spool_data(dcr)) {
473      return false;
474    }
475
476    Dmsg2(800, "Wrote block FI=%d LI=%d\n", block->FirstIndex, block->LastIndex);
477    empty_block(block);
478    return true;
479 }
480
481 static bool write_spool_header(DCR *dcr)
482 {
483    spool_hdr hdr;
484    ssize_t stat;
485    DEV_BLOCK *block = dcr->block;
486
487    hdr.FirstIndex = block->FirstIndex;
488    hdr.LastIndex = block->LastIndex;
489    hdr.len = block->binbuf;
490
491    /* Write header */
492    for (int retry=0; retry<=1; retry++) {
493       stat = write(dcr->spool_fd, (char*)&hdr, sizeof(hdr));
494       if (stat == -1) {
495          berrno be;
496          Jmsg(dcr->jcr, M_FATAL, 0, _("Error writing header to spool file. ERR=%s\n"),
497               be.bstrerror());
498       }
499       if (stat != (ssize_t)sizeof(hdr)) {
500          /* If we wrote something, truncate it, then despool */
501          if (stat != -1) {
502 #if defined(HAVE_WIN32)
503             boffset_t   pos = _lseeki64(dcr->spool_fd, (__int64)0, SEEK_CUR);
504 #else
505             boffset_t   pos = lseek(dcr->spool_fd, (off_t)0, SEEK_CUR);
506 #endif
507             if (ftruncate(dcr->spool_fd, pos - stat) != 0) {
508                berrno be;
509                Jmsg(dcr->jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
510                   be.bstrerror());
511               /* Note, try continuing despite ftruncate problem */
512             }
513          }
514          if (!despool_data(dcr, false)) {
515             Jmsg(dcr->jcr, M_FATAL, 0, _("Fatal despooling error."));
516             return false;
517          }
518          continue;                    /* try again */
519       }
520       return true;
521    }
522    Jmsg(dcr->jcr, M_FATAL, 0, _("Retrying after header spooling error failed.\n"));
523    return false;
524 }
525
526 static bool write_spool_data(DCR *dcr)
527 {
528    ssize_t stat;
529    DEV_BLOCK *block = dcr->block;
530
531    /* Write data */
532    for (int retry=0; retry<=1; retry++) {
533       stat = write(dcr->spool_fd, block->buf, (size_t)block->binbuf);
534       if (stat == -1) {
535          berrno be;
536          Jmsg(dcr->jcr, M_FATAL, 0, _("Error writing data to spool file. ERR=%s\n"),
537               be.bstrerror());
538       }
539       if (stat != (ssize_t)block->binbuf) {
540          /*
541           * If we wrote something, truncate it and the header, then despool
542           */
543          if (stat != -1) {
544 #if defined(HAVE_WIN32)
545             boffset_t   pos = _lseeki64(dcr->spool_fd, (__int64)0, SEEK_CUR);
546 #else
547             boffset_t   pos = lseek(dcr->spool_fd, (off_t)0, SEEK_CUR);
548 #endif
549             if (ftruncate(dcr->spool_fd, pos - stat - sizeof(spool_hdr)) != 0) {
550                berrno be;
551                Jmsg(dcr->jcr, M_ERROR, 0, _("Ftruncate spool file failed: ERR=%s\n"),
552                   be.bstrerror());
553                /* Note, try continuing despite ftruncate problem */
554             }
555          }
556          if (!despool_data(dcr, false)) {
557             Jmsg(dcr->jcr, M_FATAL, 0, _("Fatal despooling error."));
558             return false;
559          }
560          if (!write_spool_header(dcr)) {
561             return false;
562          }
563          continue;                    /* try again */
564       }
565       return true;
566    }
567    Jmsg(dcr->jcr, M_FATAL, 0, _("Retrying after data spooling error failed.\n"));
568    return false;
569 }
570
571
572
573 bool are_attributes_spooled(JCR *jcr)
574 {
575    return jcr->spool_attributes && jcr->dir_bsock->m_spool_fd;
576 }
577
578 /*
579  * Create spool file for attributes.
580  *  This is done by "attaching" to the bsock, and when
581  *  it is called, the output is written to a file.
582  *  The actual spooling is turned on and off in
583  *  append.c only during writing of the attributes.
584  */
585 bool begin_attribute_spool(JCR *jcr)
586 {
587    if (!jcr->no_attributes && jcr->spool_attributes) {
588       return open_attr_spool_file(jcr, jcr->dir_bsock);
589    }
590    return true;
591 }
592
593 bool discard_attribute_spool(JCR *jcr)
594 {
595    if (are_attributes_spooled(jcr)) {
596       return close_attr_spool_file(jcr, jcr->dir_bsock);
597    }
598    return true;
599 }
600
601 static void update_attr_spool_size(ssize_t size)
602 {
603    P(mutex);
604    if (size > 0) {
605      if ((spool_stats.attr_size - size) > 0) {
606         spool_stats.attr_size -= size;
607      } else {
608         spool_stats.attr_size = 0;
609      }
610    }
611    V(mutex);
612 }
613
614 bool commit_attribute_spool(JCR *jcr)
615 {
616    off_t size;
617    char ec1[30];
618
619    if (are_attributes_spooled(jcr)) {
620       if (fseeko(jcr->dir_bsock->m_spool_fd, 0, SEEK_END) != 0) {
621          berrno be;
622          Jmsg(jcr, M_FATAL, 0, _("Fseek on attributes file failed: ERR=%s\n"),
623               be.bstrerror());
624          goto bail_out;
625       }
626       size = ftello(jcr->dir_bsock->m_spool_fd);
627       if (size < 0) {
628          berrno be;
629          Jmsg(jcr, M_FATAL, 0, _("Fseek on attributes file failed: ERR=%s\n"),
630               be.bstrerror());
631          goto bail_out;
632       }
633       P(mutex);
634       if (spool_stats.attr_size + size > spool_stats.max_attr_size) {
635          spool_stats.max_attr_size = spool_stats.attr_size + size;
636       }
637       spool_stats.attr_size += size;
638       V(mutex);
639       set_jcr_job_status(jcr, JS_AttrDespooling);
640       dir_send_job_status(jcr);
641       Jmsg(jcr, M_INFO, 0, _("Sending spooled attrs to the Director. Despooling %s bytes ...\n"),
642             edit_uint64_with_commas(size, ec1));
643       jcr->dir_bsock->despool(update_attr_spool_size, size);
644       return close_attr_spool_file(jcr, jcr->dir_bsock);
645    }
646    return true;
647
648 bail_out:
649    close_attr_spool_file(jcr, jcr->dir_bsock);
650    return false;
651 }
652
653 static void make_unique_spool_filename(JCR *jcr, POOLMEM **name, int fd)
654 {
655    Mmsg(name, "%s/%s.attr.%s.%d.spool", working_directory, my_name,
656       jcr->Job, fd);
657 }
658
659
660 bool open_attr_spool_file(JCR *jcr, BSOCK *bs)
661 {
662    POOLMEM *name  = get_pool_memory(PM_MESSAGE);
663
664    make_unique_spool_filename(jcr, &name, bs->m_fd);
665    bs->m_spool_fd = fopen(name, "w+b");
666    if (!bs->m_spool_fd) {
667       berrno be;
668       Jmsg(jcr, M_FATAL, 0, _("fopen attr spool file %s failed: ERR=%s\n"), name,
669            be.bstrerror());
670       free_pool_memory(name);
671       return false;
672    }
673    P(mutex);
674    spool_stats.attr_jobs++;
675    V(mutex);
676    free_pool_memory(name);
677    return true;
678 }
679
680 bool close_attr_spool_file(JCR *jcr, BSOCK *bs)
681 {
682    POOLMEM *name;
683
684    if (!bs->m_spool_fd) {
685       return true;
686    }
687    name = get_pool_memory(PM_MESSAGE);
688    P(mutex);
689    spool_stats.attr_jobs--;
690    spool_stats.total_attr_jobs++;
691    V(mutex);
692    make_unique_spool_filename(jcr, &name, bs->m_fd);
693    fclose(bs->m_spool_fd);
694    unlink(name);
695    free_pool_memory(name);
696    bs->m_spool_fd = NULL;
697    bs->clear_spooling();
698    return true;
699 }