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