]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/backup.c
- Lots of documentation.
[bacula/bacula] / bacula / src / filed / backup.c
1 /*
2  *  Bacula File Daemon  backup.c  send file attributes and data
3  *   to the Storage daemon.
4  *
5  *    Kern Sibbald, March MM
6  *
7  *   Version $Id$
8  *
9  */
10 /*
11    Copyright (C) 2000-2005 Kern Sibbald
12
13    This program is free software; you can redistribute it and/or
14    modify it under the terms of the GNU General Public License
15    version 2 as amended with additional clauses defined in the
16    file LICENSE in the main source directory.
17
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
21    the file LICENSE for additional details.
22
23  */
24
25 #include "bacula.h"
26 #include "filed.h"
27
28 /* Forward referenced functions */
29 static int save_file(FF_PKT *ff_pkt, void *pkt, bool top_level);
30 static int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, struct CHKSUM *chksum);
31 static bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream);
32 static bool read_and_send_acl(JCR *jcr, int acltype, int stream);
33
34 /*
35  * Find all the requested files and send them
36  * to the Storage daemon.
37  *
38  * Note, we normally carry on a one-way
39  * conversation from this point on with the SD, simply blasting
40  * data to him.  To properly know what is going on, we
41  * also run a "heartbeat" monitor which reads the socket and
42  * reacts accordingly (at the moment it has nothing to do
43  * except echo the heartbeat to the Director).
44  *
45  */
46 bool blast_data_to_storage_daemon(JCR *jcr, char *addr)
47 {
48    BSOCK *sd;
49    bool ok = true;
50
51    sd = jcr->store_bsock;
52
53    set_jcr_job_status(jcr, JS_Running);
54
55    Dmsg1(300, "bfiled: opened data connection %d to stored\n", sd->fd);
56
57    LockRes();
58    CLIENT *client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
59    UnlockRes();
60    uint32_t buf_size;
61    if (client) {
62       buf_size = client->max_network_buffer_size;
63    } else {
64       buf_size = 0;                   /* use default */
65    }
66    if (!bnet_set_buffer_size(sd, buf_size, BNET_SETBUF_WRITE)) {
67       set_jcr_job_status(jcr, JS_ErrorTerminated);
68       Jmsg(jcr, M_FATAL, 0, _("Cannot set buffer size FD->SD.\n"));
69       return false;
70    }
71
72    jcr->buf_size = sd->msglen;
73    /* Adjust for compression so that output buffer is
74     * 12 bytes + 0.1% larger than input buffer plus 18 bytes.
75     * This gives a bit extra plus room for the sparse addr if any.
76     * Note, we adjust the read size to be smaller so that the
77     * same output buffer can be used without growing it.
78     */
79    jcr->compress_buf_size = jcr->buf_size + ((jcr->buf_size+999) / 1000) + 30;
80    jcr->compress_buf = get_memory(jcr->compress_buf_size);
81
82    Dmsg1(300, "set_find_options ff=%p\n", jcr->ff);
83    set_find_options((FF_PKT *)jcr->ff, jcr->incremental, jcr->mtime);
84    Dmsg0(300, "start find files\n");
85
86    start_heartbeat_monitor(jcr);
87
88    jcr->acl_text = get_pool_memory(PM_MESSAGE);
89
90    /* Subroutine save_file() is called for each file */
91    if (!find_files(jcr, (FF_PKT *)jcr->ff, save_file, (void *)jcr)) {
92       ok = false;                     /* error */
93       set_jcr_job_status(jcr, JS_ErrorTerminated);
94 //    Jmsg(jcr, M_FATAL, 0, _("Find files error.\n"));
95    }
96
97    free_pool_memory(jcr->acl_text);
98
99    bnet_sig(sd, BNET_EOD);            /* end of sending data */
100
101    stop_heartbeat_monitor(jcr);
102
103    if (jcr->big_buf) {
104       free(jcr->big_buf);
105       jcr->big_buf = NULL;
106    }
107    if (jcr->compress_buf) {
108       free_pool_memory(jcr->compress_buf);
109       jcr->compress_buf = NULL;
110    }
111    Dmsg1(100, "end blast_data ok=%d\n", ok);
112    return ok;
113 }
114
115 /*
116  * Called here by find() for each file included.
117  *   This is a callback. The original is find_files() above.
118  *
119  *  Send the file and its data to the Storage daemon.
120  *
121  *  Returns: 1 if OK
122  *           0 if error
123  *          -1 to ignore file/directory (not used here)
124  */
125 static int save_file(FF_PKT *ff_pkt, void *vjcr, bool top_level)
126 {
127    int stat, data_stream;
128    struct CHKSUM chksum;
129    BSOCK *sd;
130    JCR *jcr = (JCR *)vjcr;
131
132    if (job_canceled(jcr)) {
133       return 0;
134    }
135
136    sd = jcr->store_bsock;
137    jcr->num_files_examined++;         /* bump total file count */
138
139    switch (ff_pkt->type) {
140    case FT_LNKSAVED:                  /* Hard linked, file already saved */
141       Dmsg2(130, "FT_LNKSAVED hard link: %s => %s\n", ff_pkt->fname, ff_pkt->link);
142       break;
143    case FT_REGE:
144       Dmsg1(130, "FT_REGE saving: %s\n", ff_pkt->fname);
145       break;
146    case FT_REG:
147       Dmsg1(130, "FT_REG saving: %s\n", ff_pkt->fname);
148       break;
149    case FT_LNK:
150       Dmsg2(130, "FT_LNK saving: %s -> %s\n", ff_pkt->fname, ff_pkt->link);
151       break;
152    case FT_DIRBEGIN:
153       return 1;                       /* not used */
154    case FT_NORECURSE:
155    case FT_NOFSCHG:
156    case FT_INVALIDFS:
157    case FT_DIREND:
158       if (ff_pkt->type == FT_NORECURSE) {
159          Jmsg(jcr, M_INFO, 1, _("     Recursion turned off. Will not descend into %s\n"),
160             ff_pkt->fname);
161       } else if (ff_pkt->type == FT_NOFSCHG) {
162          Jmsg(jcr, M_INFO, 1, _("     File system change prohibited. Will not descend into %s\n"),
163             ff_pkt->fname);
164       } else if (ff_pkt->type == FT_INVALIDFS) {
165          Jmsg(jcr, M_INFO, 1, _("     Disallowed filesystem. Will not descend into %s\n"),
166             ff_pkt->fname);
167       }
168       ff_pkt->type = FT_DIREND;       /* value is used below */
169       Dmsg1(130, "FT_DIR saving: %s\n", ff_pkt->link);
170       break;
171    case FT_SPEC:
172       Dmsg1(130, "FT_SPEC saving: %s\n", ff_pkt->fname);
173       break;
174    case FT_RAW:
175       Dmsg1(130, "FT_RAW saving: %s\n", ff_pkt->fname);
176       break;
177    case FT_FIFO:
178       Dmsg1(130, "FT_FIFO saving: %s\n", ff_pkt->fname);
179       break;
180    case FT_NOACCESS: {
181       berrno be;
182       Jmsg(jcr, M_NOTSAVED, 0, _("     Could not access %s: ERR=%s\n"), ff_pkt->fname,
183          be.strerror(ff_pkt->ff_errno));
184       jcr->Errors++;
185       return 1;
186    }
187    case FT_NOFOLLOW: {
188       berrno be;
189       Jmsg(jcr, M_NOTSAVED, 0, _("     Could not follow link %s: ERR=%s\n"), ff_pkt->fname,
190          be.strerror(ff_pkt->ff_errno));
191       jcr->Errors++;
192       return 1;
193    }
194    case FT_NOSTAT: {
195       berrno be;
196       Jmsg(jcr, M_NOTSAVED, 0, _("     Could not stat %s: ERR=%s\n"), ff_pkt->fname,
197          be.strerror(ff_pkt->ff_errno));
198       jcr->Errors++;
199       return 1;
200    }
201    case FT_DIRNOCHG:
202    case FT_NOCHG:
203       Jmsg(jcr, M_SKIPPED, 1, _("     Unchanged file skipped: %s\n"), ff_pkt->fname);
204       return 1;
205    case FT_ISARCH:
206       Jmsg(jcr, M_NOTSAVED, 0, _("     Archive file not saved: %s\n"), ff_pkt->fname);
207       return 1;
208    case FT_NOOPEN: {
209       berrno be;
210       Jmsg(jcr, M_NOTSAVED, 0, _("     Could not open directory %s: ERR=%s\n"), ff_pkt->fname,
211          be.strerror(ff_pkt->ff_errno));
212       jcr->Errors++;
213       return 1;
214    }
215    default:
216       Jmsg(jcr, M_NOTSAVED, 0,  _("     Unknown file type %d; not saved: %s\n"), ff_pkt->type, ff_pkt->fname);
217       jcr->Errors++;
218       return 1;
219    }
220
221    Dmsg1(130, "bfiled: sending %s to stored\n", ff_pkt->fname);
222
223    if (!encode_and_send_attributes(jcr, ff_pkt, data_stream)) {
224       return 0;
225    }
226
227    /*
228     * Setup for signature handling.
229     * Then initialise the file descriptor we use for data and other streams.
230     */
231    chksum_init(&chksum, ff_pkt->flags);
232
233    binit(&ff_pkt->bfd);
234    if (ff_pkt->flags & FO_PORTABLE) {
235       set_portable_backup(&ff_pkt->bfd); /* disable Win32 BackupRead() */
236    }
237    if (ff_pkt->reader) {
238       if (!set_prog(&ff_pkt->bfd, ff_pkt->reader, jcr)) {
239          Jmsg(jcr, M_FATAL, 0, _("Python reader program \"%s\" not found.\n"), 
240             ff_pkt->reader);
241          return 0;
242       }
243    }
244
245    /*
246     * Open any file with data that we intend to save, then save it.
247     *
248     * Note, if is_win32_backup, we must open the Directory so that
249     * the BackupRead will save its permissions and ownership streams.
250     */
251    if (ff_pkt->type != FT_LNKSAVED && (S_ISREG(ff_pkt->statp.st_mode) &&
252          ff_pkt->statp.st_size > 0) ||
253          ff_pkt->type == FT_RAW || ff_pkt->type == FT_FIFO ||
254          (!is_portable_backup(&ff_pkt->bfd) && ff_pkt->type == FT_DIREND)) {
255       btimer_t *tid;
256       if (ff_pkt->type == FT_FIFO) {
257          tid = start_thread_timer(pthread_self(), 60);
258       } else {
259          tid = NULL;
260       }
261       if (bopen(&ff_pkt->bfd, ff_pkt->fname, O_RDONLY | O_BINARY, 0) < 0) {
262          ff_pkt->ff_errno = errno;
263          berrno be;
264          Jmsg(jcr, M_NOTSAVED, 0, _("     Cannot open %s: ERR=%s.\n"), ff_pkt->fname,
265               be.strerror());
266          jcr->Errors++;
267          if (tid) {
268             stop_thread_timer(tid);
269             tid = NULL;
270          }
271          return 1;
272       }
273       if (tid) {
274          stop_thread_timer(tid);
275          tid = NULL;
276       }
277       stat = send_data(jcr, data_stream, ff_pkt, &chksum);
278       bclose(&ff_pkt->bfd);
279       if (!stat) {
280          return 0;
281       }
282    }
283
284 #ifdef HAVE_DARWIN_OS
285    /* Regular files can have resource forks and Finder Info */
286    if (ff_pkt->type != FT_LNKSAVED && (S_ISREG(ff_pkt->statp.st_mode) &&
287             ff_pkt->flags & FO_HFSPLUS)) {
288       if (ff_pkt->hfsinfo.rsrclength > 0) {
289          int flags;
290          if (!bopen_rsrc(&ff_pkt->bfd, ff_pkt->fname, O_RDONLY | O_BINARY, 0) < 0) {
291             ff_pkt->ff_errno = errno;
292             berrno be;
293             Jmsg(jcr, M_NOTSAVED, -1, _("     Cannot open resource fork for %s: ERR=%s.\n"), ff_pkt->fname,
294                   be.strerror());
295             jcr->Errors++;
296             if (is_bopen(&ff_pkt->bfd)) {
297                bclose(&ff_pkt->bfd);
298             }
299             return 1;
300          }
301          flags = ff_pkt->flags;
302          ff_pkt->flags &= ~(FO_GZIP|FO_SPARSE);
303          stat = send_data(jcr, STREAM_MACOS_FORK_DATA, ff_pkt, &chksum);
304          ff_pkt->flags = flags;
305          bclose(&ff_pkt->bfd);
306          if (!stat) {
307             return 0;
308          }
309       }
310
311       Dmsg1(300, "Saving Finder Info for \"%s\"\n", ff_pkt->fname);
312       bnet_fsend(sd, "%ld %d 0", jcr->JobFiles, STREAM_HFSPLUS_ATTRIBUTES);
313       Dmsg1(300, "bfiled>stored:header %s\n", sd->msg);
314       memcpy(sd->msg, ff_pkt->hfsinfo.fndrinfo, 32);
315       sd->msglen = 32;
316       chksum_update(&chksum, (unsigned char *)sd->msg, sd->msglen);
317       bnet_send(sd);
318       bnet_sig(sd, BNET_EOD);
319    }
320 #endif
321
322    if (ff_pkt->flags & FO_ACL) {
323       /* Read access ACLs for files, dirs and links */
324       if (!read_and_send_acl(jcr, BACL_TYPE_ACCESS, STREAM_UNIX_ATTRIBUTES_ACCESS_ACL)) {
325          return 0;
326       }
327       /* Directories can have default ACLs too */
328       if (ff_pkt->type == FT_DIREND && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
329          if (!read_and_send_acl(jcr, BACL_TYPE_DEFAULT, STREAM_UNIX_ATTRIBUTES_DEFAULT_ACL)) {
330             return 0;
331          }
332       }
333    }
334
335    /* Terminate any signature and send it to Storage daemon and the Director */
336    if (chksum.updated) {
337       int stream = 0;
338       chksum_final(&chksum);
339       if (chksum.type == CHKSUM_MD5) {
340          stream = STREAM_MD5_SIGNATURE;
341       } else if (chksum.type == CHKSUM_SHA1) {
342          stream = STREAM_SHA1_SIGNATURE;
343       } else {
344          Jmsg1(jcr, M_WARNING, 0, _("Unknown signature type %i.\n"), chksum.type);
345       }
346       if (stream != 0) {
347          bnet_fsend(sd, "%ld %d 0", jcr->JobFiles, stream);
348          Dmsg1(300, "bfiled>stored:header %s\n", sd->msg);
349          memcpy(sd->msg, chksum.signature, chksum.length);
350          sd->msglen = chksum.length;
351          bnet_send(sd);
352          bnet_sig(sd, BNET_EOD);              /* end of checksum */
353       }
354    }
355
356    return 1;
357 }
358
359 /*
360  * Send data read from an already open file descriptor.
361  *
362  * We return 1 on sucess and 0 on errors.
363  *
364  * ***FIXME***
365  * We use ff_pkt->statp.st_size when FO_SPARSE.
366  * Currently this is not a problem as the only other stream, resource forks,
367  * are not handled as sparse files.
368  */
369 int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, struct CHKSUM *chksum)
370 {
371    BSOCK *sd = jcr->store_bsock;
372    uint64_t fileAddr = 0;             /* file address */
373    char *rbuf, *wbuf;
374    int rsize = jcr->buf_size;      /* read buffer size */
375    POOLMEM *msgsave;
376
377    msgsave = sd->msg;
378    rbuf = sd->msg;                    /* read buffer */
379    wbuf = sd->msg;                    /* write buffer */
380
381
382    Dmsg1(300, "Saving data, type=%d\n", ff_pkt->type);
383
384
385 #ifdef HAVE_LIBZ
386    uLong compress_len, max_compress_len = 0;
387    const Bytef *cbuf = NULL;
388
389    if (ff_pkt->flags & FO_GZIP) {
390       if (ff_pkt->flags & FO_SPARSE) {
391          cbuf = (Bytef *)jcr->compress_buf + SPARSE_FADDR_SIZE;
392          max_compress_len = jcr->compress_buf_size - SPARSE_FADDR_SIZE;
393       } else {
394          cbuf = (Bytef *)jcr->compress_buf;
395          max_compress_len = jcr->compress_buf_size; /* set max length */
396       }
397       wbuf = jcr->compress_buf;    /* compressed output here */
398    }
399 #endif
400
401    /*
402     * Send Data header to Storage daemon
403     *    <file-index> <stream> <info>
404     */
405    if (!bnet_fsend(sd, "%ld %d 0", jcr->JobFiles, stream)) {
406       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
407             bnet_strerror(sd));
408       return 0;
409    }
410    Dmsg1(300, ">stored: datahdr %s\n", sd->msg);
411
412    /*
413     * Make space at beginning of buffer for fileAddr because this
414     *   same buffer will be used for writing if compression if off.
415     */
416    if (ff_pkt->flags & FO_SPARSE) {
417       rbuf += SPARSE_FADDR_SIZE;
418       rsize -= SPARSE_FADDR_SIZE;
419 #ifdef HAVE_FREEBSD_OS
420       /*
421        * To read FreeBSD partitions, the read size must be
422        *  a multiple of 512.
423        */
424       rsize = (rsize/512) * 512;
425 #endif
426    }
427
428    /*
429     * Read the file data
430     */
431    while ((sd->msglen=(uint32_t)bread(&ff_pkt->bfd, rbuf, rsize)) > 0) {
432       int sparseBlock = 0;
433
434       /* Check for sparse blocks */
435       if (ff_pkt->flags & FO_SPARSE) {
436          ser_declare;
437          if (sd->msglen == rsize &&
438              (fileAddr+sd->msglen < (uint64_t)ff_pkt->statp.st_size)) {
439             sparseBlock = is_buf_zero(rbuf, rsize);
440          }
441
442          ser_begin(wbuf, SPARSE_FADDR_SIZE);
443          ser_uint64(fileAddr);     /* store fileAddr in begin of buffer */
444       }
445
446       jcr->ReadBytes += sd->msglen;         /* count bytes read */
447       fileAddr += sd->msglen;
448
449       /* Update checksum if requested */
450       chksum_update(chksum, (unsigned char *)rbuf, sd->msglen);
451
452 #ifdef HAVE_LIBZ
453       /* Do compression if turned on */
454       if (!sparseBlock && ff_pkt->flags & FO_GZIP) {
455          int zstat;
456          compress_len = max_compress_len;
457          Dmsg4(400, "cbuf=0x%x len=%u rbuf=0x%x len=%u\n", cbuf, compress_len,
458             rbuf, sd->msglen);
459          /* NOTE! This call modifies compress_len !!! */
460          if ((zstat=compress2((Bytef *)cbuf, &compress_len,
461                (const Bytef *)rbuf, (uLong)sd->msglen,
462                ff_pkt->GZIP_level)) != Z_OK) {
463             Jmsg(jcr, M_FATAL, 0, _("Compression error: %d\n"), zstat);
464             sd->msg = msgsave;
465             sd->msglen = 0;
466             set_jcr_job_status(jcr, JS_ErrorTerminated);
467             return 0;
468          }
469          Dmsg2(400, "compressed len=%d uncompressed len=%d\n",
470             compress_len, sd->msglen);
471
472          sd->msglen = compress_len;      /* set compressed length */
473       }
474 #endif
475
476       /* Send the buffer to the Storage daemon */
477       if (!sparseBlock) {
478          if (ff_pkt->flags & FO_SPARSE) {
479             sd->msglen += SPARSE_FADDR_SIZE; /* include fileAddr in size */
480          }
481          sd->msg = wbuf;              /* set correct write buffer */
482          if (!bnet_send(sd)) {
483             Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
484                   bnet_strerror(sd));
485             sd->msg = msgsave;     /* restore bnet buffer */
486             sd->msglen = 0;
487             return 0;
488          }
489       }
490       Dmsg1(130, "Send data to SD len=%d\n", sd->msglen);
491       /*          #endif */
492       jcr->JobBytes += sd->msglen;      /* count bytes saved possibly compressed */
493       sd->msg = msgsave;                /* restore read buffer */
494
495    } /* end while read file data */
496
497
498    if (sd->msglen < 0) {
499       berrno be;
500       Jmsg(jcr, M_ERROR, 0, _("Read error on file %s. ERR=%s\n"),
501          ff_pkt->fname, be.strerror(ff_pkt->bfd.berrno));
502       if (jcr->Errors++ > 1000) {       /* insanity check */
503          Jmsg(jcr, M_FATAL, 0, _("Too many errors.\n"));
504       }
505
506    }
507
508    if (!bnet_sig(sd, BNET_EOD)) {        /* indicate end of file data */
509       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
510             bnet_strerror(sd));
511       return 0;
512    }
513
514    return 1;
515 }
516
517 /*
518  * Read and send an ACL for the last encountered file.
519  */
520 static bool read_and_send_acl(JCR *jcr, int acltype, int stream)
521 {
522 #ifdef HAVE_ACL
523    BSOCK *sd = jcr->store_bsock;
524    POOLMEM *msgsave;
525    int len;
526
527    len = bacl_get(jcr, acltype);
528    if (len < 0) {
529       Jmsg1(jcr, M_WARNING, 0, "Error reading ACL of %s\n", jcr->last_fname);
530       return true; 
531    }
532    if (len == 0) {
533       return true;                    /* no ACL */
534    }
535
536    /* Send header */
537    if (!bnet_fsend(sd, "%ld %d 0", jcr->JobFiles, stream)) {
538       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
539             bnet_strerror(sd));
540       return false;
541    }
542
543    /* Send the buffer to the storage deamon */
544    Dmsg2(400, "Backing up ACL type 0x%2x <%s>\n", acltype, jcr->acl_text);
545    msgsave = sd->msg;
546    sd->msg = jcr->acl_text;
547    sd->msglen = len + 1;
548    if (!bnet_send(sd)) {
549       sd->msg = msgsave;
550       sd->msglen = 0;
551       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
552             bnet_strerror(sd));
553       return false;
554    }
555
556    jcr->JobBytes += sd->msglen;
557    sd->msg = msgsave;
558    if (!bnet_sig(sd, BNET_EOD)) {
559       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
560             bnet_strerror(sd));
561       return false;
562    }
563
564    Dmsg1(200, "ACL of file: %s successfully backed up!\n", jcr->last_fname);
565 #endif
566    return true;
567 }
568
569 static bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream) 
570 {
571    BSOCK *sd = jcr->store_bsock;
572    char attribs[MAXSTRING];
573    char attribsEx[MAXSTRING];
574    int attr_stream;
575    int stat;
576 #ifdef FD_NO_SEND_TEST
577    return true;
578 #endif
579
580    /* Find what data stream we will use, then encode the attributes */
581    data_stream = select_data_stream(ff_pkt);
582    encode_stat(attribs, ff_pkt, data_stream);
583
584    /* Now possibly extend the attributes */
585    attr_stream = encode_attribsEx(jcr, attribsEx, ff_pkt);
586
587    Dmsg3(300, "File %s\nattribs=%s\nattribsEx=%s\n", ff_pkt->fname, attribs, attribsEx);
588
589    P(jcr->mutex);
590    jcr->JobFiles++;                    /* increment number of files sent */
591    ff_pkt->FileIndex = jcr->JobFiles;  /* return FileIndex */
592    pm_strcpy(jcr->last_fname, ff_pkt->fname);
593    V(jcr->mutex);
594
595    /*
596     * Send Attributes header to Storage daemon
597     *    <file-index> <stream> <info>
598     */
599    if (!bnet_fsend(sd, "%ld %d 0", jcr->JobFiles, attr_stream)) {
600       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
601             bnet_strerror(sd));
602       return false;
603    }
604    Dmsg1(300, ">stored: attrhdr %s\n", sd->msg);
605
606    /*
607     * Send file attributes to Storage daemon
608     *   File_index
609     *   File type
610     *   Filename (full path)
611     *   Encoded attributes
612     *   Link name (if type==FT_LNK or FT_LNKSAVED)
613     *   Encoded extended-attributes (for Win32)
614     *
615     * For a directory, link is the same as fname, but with trailing
616     * slash. For a linked file, link is the link.
617     */
618    if (ff_pkt->type == FT_LNK || ff_pkt->type == FT_LNKSAVED) {
619       Dmsg2(300, "Link %s to %s\n", ff_pkt->fname, ff_pkt->link);
620       stat = bnet_fsend(sd, "%ld %d %s%c%s%c%s%c%s%c", jcr->JobFiles,
621                ff_pkt->type, ff_pkt->fname, 0, attribs, 0, ff_pkt->link, 0,
622                attribsEx, 0);
623    } else if (ff_pkt->type == FT_DIREND) {
624       /* Here link is the canonical filename (i.e. with trailing slash) */
625       stat = bnet_fsend(sd, "%ld %d %s%c%s%c%c%s%c", jcr->JobFiles,
626                ff_pkt->type, ff_pkt->link, 0, attribs, 0, 0, attribsEx, 0);
627    } else {
628       stat = bnet_fsend(sd, "%ld %d %s%c%s%c%c%s%c", jcr->JobFiles,
629                ff_pkt->type, ff_pkt->fname, 0, attribs, 0, 0, attribsEx, 0);
630    }
631
632    Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg);
633    if (!stat) {
634       Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
635             bnet_strerror(sd));
636       return false;
637    }
638    bnet_sig(sd, BNET_EOD);            /* indicate end of attributes data */
639    return true;
640 }