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