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