1 /*
2 Bacula(R) - The Network Backup Solution
3
4 Copyright (C) 2000-2020 Kern Sibbald
5
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
8
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
13
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
16
17 Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20
21 This file was derived from GNU TAR source code. Except for a few key
22 ideas, it has been entirely rewritten for Bacula.
23
24 Kern Sibbald, MM
25
26 Thanks to the TAR programmers.
27
28 */
29
30 #include "bacula.h"
31 #include "find.h"
32 #ifdef HAVE_DARWIN_OS
33 #include <sys/param.h>
34 #include <sys/mount.h>
35 #include <sys/attr.h>
36 #endif
37
38 int breaddir(DIR *dirp, POOLMEM *&d_name);
39
40 extern int32_t name_max; /* filename max length */
41 extern int32_t path_max; /* path name max length */
42
43 /*
44 * Structure for keeping track of hard linked files, we
45 * keep an entry for each hardlinked file that we save,
46 * which is the first one found. For all the other files that
47 * are linked to this one, we save only the directory
48 * entry so we can link it.
49 */
50 struct f_link {
51 struct f_link *next;
52 dev_t dev; /* device */
53 ino_t ino; /* inode with device is unique */
54 int32_t FileIndex; /* Bacula FileIndex of this file */
55 int32_t digest_stream; /* Digest type if needed */
56 uint32_t digest_len; /* Digest len if needed */
57 char *digest; /* Checksum of the file if needed */
58 char name[1]; /* The name */
59 };
60
61 typedef struct f_link link_t;
62 #define LINK_HASHTABLE_BITS 16
63 #define LINK_HASHTABLE_SIZE (1<<LINK_HASHTABLE_BITS)
64 #define LINK_HASHTABLE_MASK (LINK_HASHTABLE_SIZE-1)
65
LINKHASH(const struct stat & info)66 static inline int LINKHASH(const struct stat &info)
67 {
68 int hash = info.st_dev;
69 unsigned long long i = info.st_ino;
70 hash ^= i;
71 i >>= 16;
72 hash ^= i;
73 i >>= 16;
74 hash ^= i;
75 i >>= 16;
76 hash ^= i;
77 return hash & LINK_HASHTABLE_MASK;
78 }
79
80 /*
81 * Create a new directory Find File packet, but copy
82 * some of the essential info from the current packet.
83 * However, be careful to zero out the rest of the
84 * packet.
85 */
new_dir_ff_pkt(FF_PKT * ff_pkt)86 static FF_PKT *new_dir_ff_pkt(FF_PKT *ff_pkt)
87 {
88 FF_PKT *dir_ff_pkt = (FF_PKT *)bmalloc(sizeof(FF_PKT));
89 memcpy(dir_ff_pkt, ff_pkt, sizeof(FF_PKT));
90
91 /* Do not duplicate pointers */
92 dir_ff_pkt->fname = bstrdup(ff_pkt->fname);
93 dir_ff_pkt->snap_fname = bstrdup(ff_pkt->snap_fname);
94 dir_ff_pkt->link = bstrdup(ff_pkt->link);
95
96 if (ff_pkt->fname_save) {
97 dir_ff_pkt->fname_save = get_pool_memory(PM_FNAME);
98 pm_strcpy(dir_ff_pkt->fname_save, ff_pkt->fname_save);
99 }
100 if (ff_pkt->link_save) {
101 dir_ff_pkt->link_save = get_pool_memory(PM_FNAME);
102 pm_strcpy(dir_ff_pkt->link_save, ff_pkt->link_save);
103 }
104
105 dir_ff_pkt->included_files_list = NULL;
106 dir_ff_pkt->excluded_files_list = NULL;
107 dir_ff_pkt->excluded_paths_list = NULL;
108 dir_ff_pkt->linkhash = NULL;
109 dir_ff_pkt->ignoredir_fname = NULL;
110 return dir_ff_pkt;
111 }
112
113 /*
114 * Free the temp directory ff_pkt
115 */
free_dir_ff_pkt(FF_PKT * dir_ff_pkt)116 static void free_dir_ff_pkt(FF_PKT *dir_ff_pkt)
117 {
118 free(dir_ff_pkt->fname);
119 free(dir_ff_pkt->snap_fname);
120 free(dir_ff_pkt->link);
121 if (dir_ff_pkt->fname_save) {
122 free_pool_memory(dir_ff_pkt->fname_save);
123 }
124 if (dir_ff_pkt->link_save) {
125 free_pool_memory(dir_ff_pkt->link_save);
126 }
127 free(dir_ff_pkt);
128 }
129
130 /*
131 * Check to see if we allow the file system type of a file or directory.
132 * If we do not have a list of file system types, we accept anything.
133 */
accept_fstype(FF_PKT * ff,void * dummy)134 static int accept_fstype(FF_PKT *ff, void *dummy) {
135 int i;
136 char fs[1000];
137 bool accept = true;
138
139 if (ff->fstypes.size()) {
140 accept = false;
141 if (!fstype(ff, fs, sizeof(fs))) {
142 Dmsg1(50, "Cannot determine file system type for \"%s\"\n", ff->fname);
143 } else {
144 for (i = 0; i < ff->fstypes.size(); ++i) {
145 if (strcmp(fs, (char *)ff->fstypes.get(i)) == 0) {
146 Dmsg2(100, "Accepting fstype %s for \"%s\"\n", fs, ff->fname);
147 accept = true;
148 break;
149 }
150 Dmsg3(200, "fstype %s for \"%s\" does not match %s\n", fs,
151 ff->fname, ff->fstypes.get(i));
152 }
153 }
154 }
155 return accept;
156 }
157
158 /*
159 * Check to see if we allow the drive type of a file or directory.
160 * If we do not have a list of drive types, we accept anything.
161 */
accept_drivetype(FF_PKT * ff,void * dummy)162 static int accept_drivetype(FF_PKT *ff, void *dummy) {
163 int i;
164 char dt[100];
165 bool accept = true;
166
167 if (ff->drivetypes.size()) {
168 accept = false;
169 /* fname is a better choice here than snap_fname */
170 if (!drivetype(ff->fname, dt, sizeof(dt))) {
171 Dmsg1(50, "Cannot determine drive type for \"%s\"\n", ff->fname);
172 } else {
173 for (i = 0; i < ff->drivetypes.size(); ++i) {
174 if (strcmp(dt, (char *)ff->drivetypes.get(i)) == 0) {
175 Dmsg2(100, "Accepting drive type %s for \"%s\"\n", dt, ff->fname);
176 accept = true;
177 break;
178 }
179 Dmsg3(200, "drive type %s for \"%s\" does not match %s\n", dt,
180 ff->fname, ff->drivetypes.get(i));
181 }
182 }
183 }
184 return accept;
185 }
186
187 /*
188 * This function determines whether we can use getattrlist()
189 * It's odd, but we have to use the function to determine that...
190 * Also, the man pages talk about things as if they were implemented.
191 *
192 * On Mac OS X, this succesfully differentiates between HFS+ and UFS
193 * volumes, which makes me trust it is OK for others, too.
194 */
volume_has_attrlist(const char * fname)195 static bool volume_has_attrlist(const char *fname)
196 {
197 #ifdef HAVE_DARWIN_OS
198 struct statfs st;
199 struct volinfo_struct {
200 unsigned long length; /* Mandatory field */
201 vol_capabilities_attr_t info; /* Volume capabilities */
202 } vol;
203 struct attrlist attrList;
204
205 memset(&attrList, 0, sizeof(attrList));
206 attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
207 attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_CAPABILITIES;
208 if (statfs(fname, &st) == 0) {
209 /* We need to check on the mount point */
210 if (getattrlist(st.f_mntonname, &attrList, &vol, sizeof(vol), FSOPT_NOFOLLOW) == 0
211 && (vol.info.capabilities[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST)
212 && (vol.info.valid[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST)) {
213 return true;
214 }
215 }
216 #endif
217 return false;
218 }
219
220 /*
221 * check for BSD nodump flag
222 */
no_dump(JCR * jcr,FF_PKT * ff_pkt)223 static bool no_dump(JCR *jcr, FF_PKT *ff_pkt)
224 {
225 #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
226 if ( (ff_pkt->flags & FO_HONOR_NODUMP) &&
227 (ff_pkt->statp.st_flags & UF_NODUMP) ) {
228 Jmsg(jcr, M_INFO, 1, _(" NODUMP flag set - will not process %s\n"),
229 ff_pkt->fname);
230 return true; /* do not backup this file */
231 }
232 #endif
233 return false; /* do backup */
234 }
235
236 /* check if a file have changed during backup and display an error */
has_file_changed(JCR * jcr,FF_PKT * ff_pkt)237 bool has_file_changed(JCR *jcr, FF_PKT *ff_pkt)
238 {
239 struct stat statp;
240 Dmsg1(500, "has_file_changed fname=%s\n",ff_pkt->fname);
241
242 if (ff_pkt->snapshot_convert_fct) {
243 /* their is no reason to check for a file change inside a snapshot */
244 /* but this has already highlighted some bugs in the past */
245 /* if you want to optimize, uncomment the "return below" */
246 // return false;
247 }
248
249 if (ff_pkt->type != FT_REG) { /* not a regular file */
250 return false;
251 }
252
253 if (lstat(ff_pkt->snap_fname, &statp) != 0) {
254 berrno be;
255 Jmsg(jcr, M_WARNING, 0,
256 _("Cannot stat file %s: ERR=%s\n"),ff_pkt->fname,be.bstrerror());
257 return true;
258 }
259
260 if (statp.st_mtime != ff_pkt->statp.st_mtime) {
261 Jmsg(jcr, M_ERROR, 0, _("%s mtime changed during backup.\n"), ff_pkt->fname);
262 Dmsg3(50, "%s mtime (%lld) changed during backup (%lld).\n", ff_pkt->fname,
263 (int64_t)ff_pkt->statp.st_mtime, (int64_t)statp.st_mtime);
264 return true;
265 }
266
267 if (statp.st_ctime != ff_pkt->statp.st_ctime) {
268 Jmsg(jcr, M_ERROR, 0, _("%s ctime changed during backup.\n"), ff_pkt->fname);
269 Dmsg3(50, "%s ctime (%lld) changed during backup (%lld).\n", ff_pkt->fname,
270 (int64_t)ff_pkt->statp.st_ctime, (int64_t)statp.st_ctime);
271 return true;
272 }
273
274 if ((int64_t)statp.st_size != (int64_t)ff_pkt->statp.st_size) {
275 Jmsg(jcr, M_ERROR, 0, _("%s size of %lld changed during backup to %lld.n"),ff_pkt->fname,
276 (int64_t)ff_pkt->statp.st_size, (int64_t)statp.st_size);
277 Dmsg3(50, "%s size (%lld) changed during backup (%lld).\n", ff_pkt->fname,
278 (int64_t)ff_pkt->statp.st_size, (int64_t)statp.st_size);
279 return true;
280 }
281
282 return false;
283 }
284
285 /*
286 * For incremental/differential or accurate backups, we
287 * determine if the current file has changed.
288 */
check_changes(JCR * jcr,FF_PKT * ff_pkt)289 bool check_changes(JCR *jcr, FF_PKT *ff_pkt)
290 {
291 /* in special mode (like accurate backup), the programmer can
292 * choose his comparison function.
293 */
294 if (ff_pkt->check_fct) {
295 return ff_pkt->check_fct(jcr, ff_pkt);
296 }
297
298 /* For normal backups (incr/diff), we use this default
299 * behaviour
300 */
301 if (ff_pkt->incremental &&
302 (ff_pkt->statp.st_mtime < ff_pkt->save_time &&
303 ((ff_pkt->flags & FO_MTIMEONLY) ||
304 ff_pkt->statp.st_ctime < ff_pkt->save_time)))
305 {
306 return false;
307 }
308
309 return true;
310 }
311
have_ignoredir(FF_PKT * ff_pkt)312 static bool have_ignoredir(FF_PKT *ff_pkt)
313 {
314 struct stat sb;
315 char *ignoredir;
316
317 /* Ensure that pointers are defined */
318 if (!ff_pkt->fileset || !ff_pkt->fileset->incexe) {
319 return false;
320 }
321 ignoredir = ff_pkt->fileset->incexe->ignoredir;
322
323 if (ignoredir) {
324 if (!ff_pkt->ignoredir_fname) {
325 ff_pkt->ignoredir_fname = get_pool_memory(PM_FNAME);
326 }
327 Mmsg(ff_pkt->ignoredir_fname, "%s/%s", ff_pkt->fname, ignoredir);
328 if (stat(ff_pkt->ignoredir_fname, &sb) == 0) {
329 Dmsg2(100, "Directory '%s' ignored (found %s)\n",
330 ff_pkt->fname, ignoredir);
331 return true; /* Just ignore this directory */
332 }
333 }
334 return false;
335 }
336
337 /*
338 * When the current file is a hardlink, the backup code can compute
339 * the checksum and store it into the link_t structure.
340 */
341 void
ff_pkt_set_link_digest(FF_PKT * ff_pkt,int32_t digest_stream,const char * digest,uint32_t len)342 ff_pkt_set_link_digest(FF_PKT *ff_pkt,
343 int32_t digest_stream, const char *digest, uint32_t len)
344 {
345 if (ff_pkt->linked && !ff_pkt->linked->digest) { /* is a hardlink */
346 ff_pkt->linked->digest = (char *) bmalloc(len);
347 memcpy(ff_pkt->linked->digest, digest, len);
348 ff_pkt->linked->digest_len = len;
349 ff_pkt->linked->digest_stream = digest_stream;
350 }
351 }
352
353 /*
354 * Find a single file.
355 * handle_file is the callback for handling the file.
356 * p is the filename
357 * parent_device is the device we are currently on
358 * top_level is 1 when not recursing or 0 when
359 * descending into a directory.
360 */
361 int
find_one_file(JCR * jcr,FF_PKT * ff_pkt,int handle_file (JCR * jcr,FF_PKT * ff,bool top_level),char * fname,char * snap_fname,dev_t parent_device,bool top_level)362 find_one_file(JCR *jcr, FF_PKT *ff_pkt,
363 int handle_file(JCR *jcr, FF_PKT *ff, bool top_level),
364 char *fname, char *snap_fname, dev_t parent_device, bool top_level)
365 {
366 struct utimbuf restore_times;
367 int rtn_stat;
368 int len;
369
370 ff_pkt->fname = ff_pkt->link = fname;
371 ff_pkt->snap_fname = snap_fname;
372
373 if (lstat(snap_fname, &ff_pkt->statp) != 0) {
374 /* Cannot stat file */
375 ff_pkt->type = FT_NOSTAT;
376 ff_pkt->ff_errno = errno;
377 return handle_file(jcr, ff_pkt, top_level);
378 }
379
380 Dmsg1(300, "File ----: %s\n", fname);
381
382 /* Save current times of this directory in case we need to
383 * reset them because the user doesn't want them changed.
384 */
385 restore_times.actime = ff_pkt->statp.st_atime;
386 restore_times.modtime = ff_pkt->statp.st_mtime;
387
388 /*
389 * We check for allowed fstypes and drivetypes at top_level and fstype change (below).
390 */
391 if (top_level) {
392 if (!accept_fstype(ff_pkt, NULL)) {
393 ff_pkt->type = FT_INVALIDFS;
394 if (ff_pkt->flags & FO_KEEPATIME && !ff_pkt->snapshot_convert_fct) {
395 utime(snap_fname, &restore_times); /* snap_fname == fname */
396 }
397
398 char fs[100];
399
400 if (!fstype(ff_pkt, fs, sizeof(fs))) {
401 bstrncpy(fs, "unknown", sizeof(fs));
402 }
403
404 Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has unlisted fstype \"%s\"\n"), fname, fs);
405 return 1; /* Just ignore this error - or the whole backup is cancelled */
406 }
407 if (!accept_drivetype(ff_pkt, NULL)) {
408 ff_pkt->type = FT_INVALIDDT;
409 if (ff_pkt->flags & FO_KEEPATIME && !ff_pkt->snapshot_convert_fct) {
410 utime(snap_fname, &restore_times); /* snap_fname == fname */
411 }
412
413 char dt[100];
414
415 if (!drivetype(ff_pkt->fname, dt, sizeof(dt))) {
416 bstrncpy(dt, "unknown", sizeof(dt));
417 }
418
419 Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has an unlisted drive type \"%s\"\n"), fname, dt);
420 return 1; /* Just ignore this error - or the whole backup is cancelled */
421 }
422 ff_pkt->volhas_attrlist = volume_has_attrlist(snap_fname);
423 }
424
425 /*
426 * Ignore this entry if no_dump() returns true
427 */
428 if (no_dump(jcr, ff_pkt)) {
429 Dmsg1(100, "'%s' ignored (NODUMP flag set)\n",
430 ff_pkt->fname);
431 return 1;
432 }
433
434 /*
435 * If this is an Incremental backup, see if file was modified
436 * since our last "save_time", presumably the last Full save
437 * or Incremental.
438 */
439 if ( !S_ISDIR(ff_pkt->statp.st_mode)
440 && !check_changes(jcr, ff_pkt))
441 {
442 Dmsg1(500, "Non-directory incremental: %s\n", ff_pkt->fname);
443 ff_pkt->type = FT_NOCHG;
444 return handle_file(jcr, ff_pkt, top_level);
445 }
446
447 #ifdef HAVE_DARWIN_OS
448 if (ff_pkt->flags & FO_HFSPLUS && ff_pkt->volhas_attrlist
449 && S_ISREG(ff_pkt->statp.st_mode)) {
450 /* TODO: initialise attrList once elsewhere? */
451 struct attrlist attrList;
452 memset(&attrList, 0, sizeof(attrList));
453 attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
454 attrList.commonattr = ATTR_CMN_FNDRINFO;
455 attrList.fileattr = ATTR_FILE_RSRCLENGTH;
456 if (getattrlist(snap_fname, &attrList, &ff_pkt->hfsinfo,
457 sizeof(ff_pkt->hfsinfo), FSOPT_NOFOLLOW) != 0) {
458 ff_pkt->type = FT_NOSTAT;
459 ff_pkt->ff_errno = errno;
460 return handle_file(jcr, ff_pkt, top_level);
461 }
462 return -1; /* ignore */
463 }
464 #endif
465
466 ff_pkt->LinkFI = 0;
467 /*
468 * Handle hard linked files
469 *
470 * Maintain a list of hard linked files already backed up. This
471 * allows us to ensure that the data of each file gets backed
472 * up only once.
473 */
474 if (!(ff_pkt->flags & FO_NO_HARDLINK)
475 && ff_pkt->statp.st_nlink > 1
476 && (S_ISREG(ff_pkt->statp.st_mode)
477 || S_ISCHR(ff_pkt->statp.st_mode)
478 || S_ISBLK(ff_pkt->statp.st_mode)
479 || S_ISFIFO(ff_pkt->statp.st_mode)
480 || S_ISSOCK(ff_pkt->statp.st_mode))) {
481
482 struct f_link *lp;
483 if (ff_pkt->linkhash == NULL) {
484 ff_pkt->linkhash = (link_t **)bmalloc(LINK_HASHTABLE_SIZE * sizeof(link_t *));
485 memset(ff_pkt->linkhash, 0, LINK_HASHTABLE_SIZE * sizeof(link_t *));
486 }
487 const int linkhash = LINKHASH(ff_pkt->statp);
488
489 /* Search link list of hard linked files */
490 for (lp = ff_pkt->linkhash[linkhash]; lp; lp = lp->next)
491 if (lp->ino == (ino_t)ff_pkt->statp.st_ino &&
492 lp->dev == (dev_t)ff_pkt->statp.st_dev) {
493 /* If we have already backed up the hard linked file don't do it again */
494 if (strcmp(lp->name, fname) == 0) {
495 Dmsg2(400, "== Name identical skip FI=%d file=%s\n", lp->FileIndex, fname);
496 return 1; /* ignore */
497 }
498 ff_pkt->link = lp->name;
499 ff_pkt->type = FT_LNKSAVED; /* Handle link, file already saved */
500 ff_pkt->LinkFI = lp->FileIndex;
501 ff_pkt->linked = 0;
502 ff_pkt->digest = lp->digest;
503 ff_pkt->digest_stream = lp->digest_stream;
504 ff_pkt->digest_len = lp->digest_len;
505 rtn_stat = handle_file(jcr, ff_pkt, top_level);
506 Dmsg3(400, "FT_LNKSAVED FI=%d LinkFI=%d file=%s\n",
507 ff_pkt->FileIndex, lp->FileIndex, lp->name);
508 return rtn_stat;
509 }
510
511 /* File not previously dumped. Chain it into our list. */
512 len = strlen(fname) + 1;
513 lp = (struct f_link *)bmalloc(sizeof(struct f_link) + len);
514 lp->digest = NULL; /* set later */
515 lp->digest_stream = 0; /* set later */
516 lp->digest_len = 0; /* set later */
517 lp->ino = ff_pkt->statp.st_ino;
518 lp->dev = ff_pkt->statp.st_dev;
519 lp->FileIndex = 0; /* set later */
520 bstrncpy(lp->name, fname, len);
521 lp->next = ff_pkt->linkhash[linkhash];
522 ff_pkt->linkhash[linkhash] = lp;
523 ff_pkt->linked = lp; /* mark saved link */
524 Dmsg2(400, "added to hash FI=%d file=%s\n", ff_pkt->FileIndex, lp->name);
525 } else {
526 ff_pkt->linked = NULL;
527 }
528
529 /* This is not a link to a previously dumped file, so dump it. */
530 if (S_ISREG(ff_pkt->statp.st_mode)) {
531 boffset_t sizeleft;
532
533 sizeleft = ff_pkt->statp.st_size;
534
535 /* Don't bother opening empty, world readable files. Also do not open
536 files when archive is meant for /dev/null. */
537 if (ff_pkt->null_output_device || (sizeleft == 0
538 && MODE_RALL == (MODE_RALL & ff_pkt->statp.st_mode))) {
539 ff_pkt->type = FT_REGE;
540 } else {
541 ff_pkt->type = FT_REG;
542 }
543 rtn_stat = handle_file(jcr, ff_pkt, top_level);
544 if (ff_pkt->linked) {
545 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
546 }
547 Dmsg3(400, "FT_REG FI=%d linked=%d file=%s\n", ff_pkt->FileIndex,
548 ff_pkt->linked ? 1 : 0, fname);
549 if (ff_pkt->flags & FO_KEEPATIME && !ff_pkt->snapshot_convert_fct) {
550 utime(snap_fname, &restore_times); /* snap_fname == fname */
551 }
552 return rtn_stat;
553
554
555 } else if (S_ISLNK(ff_pkt->statp.st_mode)) { /* soft link */
556 int size;
557 char *buffer = (char *)alloca(path_max + name_max + 102);
558
559 size = readlink(snap_fname, buffer, path_max + name_max + 101);
560 if (size < 0) {
561 /* Could not follow link */
562 ff_pkt->type = FT_NOFOLLOW;
563 ff_pkt->ff_errno = errno;
564 rtn_stat = handle_file(jcr, ff_pkt, top_level);
565 if (ff_pkt->linked) {
566 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
567 }
568 return rtn_stat;
569 }
570 buffer[size] = 0;
571 ff_pkt->link = buffer; /* point to link */
572 ff_pkt->type = FT_LNK; /* got a real link */
573 rtn_stat = handle_file(jcr, ff_pkt, top_level);
574 if (ff_pkt->linked) {
575 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
576 }
577 return rtn_stat;
578
579 } else if (S_ISDIR(ff_pkt->statp.st_mode)) {
580 DIR *directory;
581 POOL_MEM dname(PM_FNAME);
582 char *link;
583 int link_len;
584 int len;
585 int status;
586 dev_t our_device = ff_pkt->statp.st_dev;
587 bool recurse = true;
588 bool volhas_attrlist = ff_pkt->volhas_attrlist; /* Remember this if we recurse */
589
590 /*
591 * Ignore this directory and everything below if the file .nobackup
592 * (or what is defined for IgnoreDir in this fileset) exists
593 */
594 if (have_ignoredir(ff_pkt)) {
595 return 1; /* Just ignore this directory */
596 }
597
598 /* Build a canonical directory name with a trailing slash in link var */
599 len = strlen(fname);
600 link_len = len + 200;
601 link = (char *)bmalloc(link_len + 2);
602 bstrncpy(link, fname, link_len);
603 /* Strip all trailing slashes */
604 while (len >= 1 && IsPathSeparator(link[len - 1]))
605 len--;
606 link[len++] = '/'; /* add back one */
607 link[len] = 0;
608
609 ff_pkt->link = link;
610 if (!check_changes(jcr, ff_pkt)) {
611 /* Incremental/Full+Base option, directory entry not changed */
612 ff_pkt->type = FT_DIRNOCHG;
613 } else {
614 ff_pkt->type = FT_DIRBEGIN;
615 }
616 /*
617 * We have set st_rdev to 1 if it is a reparse point, otherwise 0,
618 * if st_rdev is 2, it is a mount point
619 */
620 #if defined(HAVE_WIN32)
621 /*
622 * A reparse point (WIN32_REPARSE_POINT)
623 * is something special like one of the following:
624 * IO_REPARSE_TAG_DFS 0x8000000A
625 * IO_REPARSE_TAG_DFSR 0x80000012
626 * IO_REPARSE_TAG_HSM 0xC0000004
627 * IO_REPARSE_TAG_HSM2 0x80000006
628 * IO_REPARSE_TAG_SIS 0x80000007
629 * IO_REPARSE_TAG_SYMLINK 0xA000000C
630 *
631 * A junction point is a:
632 * IO_REPARSE_TAG_MOUNT_POINT 0xA0000003
633 * which can be either a link to a Volume (WIN32_MOUNT_POINT)
634 * or a link to a directory (WIN32_JUNCTION_POINT)
635 *
636 * Ignore WIN32_REPARSE_POINT and WIN32_JUNCTION_POINT
637 */
638 if (ff_pkt->statp.st_rdev == WIN32_REPARSE_POINT) {
639 ff_pkt->type = FT_REPARSE;
640 } else if (ff_pkt->statp.st_rdev == WIN32_JUNCTION_POINT ||
641 (ff_pkt->statp.st_rdev == WIN32_SYMLINK_POINT && S_ISDIR(ff_pkt->statp.st_mode))) {
642 // Handle SYMLINK DIRECTORY like a Junction, BackupRead() does all the work
643 ff_pkt->type = FT_JUNCTION;
644 }
645 #endif
646 /*
647 * Note, we return the directory to the calling program (handle_file)
648 * when we first see the directory (FT_DIRBEGIN.
649 * This allows the program to apply matches and make a
650 * choice whether or not to accept it. If it is accepted, we
651 * do not immediately save it, but do so only after everything
652 * in the directory is seen (i.e. the FT_DIREND).
653 */
654 rtn_stat = handle_file(jcr, ff_pkt, top_level);
655 if (rtn_stat < 1 || ff_pkt->type == FT_REPARSE ||
656 ff_pkt->type == FT_JUNCTION) { /* ignore or error status */
657 free(link);
658 return rtn_stat;
659 }
660 /* Done with DIRBEGIN, next call will be DIREND */
661 if (ff_pkt->type == FT_DIRBEGIN) {
662 ff_pkt->type = FT_DIREND;
663 }
664
665 /*
666 * Create a temporary ff packet for this directory
667 * entry, and defer handling the directory until
668 * we have recursed into it. This saves the
669 * directory after all files have been processed, and
670 * during the restore, the directory permissions will
671 * be reset after all the files have been restored.
672 */
673 Dmsg1(300, "Create temp ff packet for dir: %s\n", ff_pkt->fname);
674 FF_PKT *dir_ff_pkt = new_dir_ff_pkt(ff_pkt);
675 #if defined(HAVE_WIN32)
676 if (dir_ff_pkt->root_of_volume || (ff_pkt->fname[3]=='\0' && ff_pkt->fname[1]==':')) {
677 /* tell "restore" that this is a root directory and that it should not
678 * restore the Windows hidden and system attribute
679 */
680 dir_ff_pkt->statp.st_rdev = WIN32_ROOT_POINT;
681 // don't "propagate" the "root_of_volume" property to sub-directories
682 ff_pkt->root_of_volume = false;
683 }
684 #endif
685
686 /*
687 * Do not descend into subdirectories (recurse) if the
688 * user has turned it off for this directory.
689 *
690 * If we are crossing file systems, we are either not allowed
691 * to cross, or we may be restricted by a list of permitted
692 * file systems.
693 */
694 bool is_win32_mount_point = false;
695 #if defined(HAVE_WIN32)
696 is_win32_mount_point = ff_pkt->statp.st_rdev == WIN32_MOUNT_POINT;
697 #endif
698 if (!top_level && ff_pkt->flags & FO_NO_RECURSION) {
699 ff_pkt->type = FT_NORECURSE;
700 recurse = false;
701 } else if (!top_level &&
702 (parent_device != ff_pkt->statp.st_dev || is_win32_mount_point)) {
703 /* Nested mountpoint in a Windows snapshot works like the original
704 * mountpoint and redirect to the Live filesystem (not a snapshot)
705 *
706 * In Linux the directory is not a mountpoint anymore and we backup
707 * the content of the directory that is normally empty.
708 */
709 if (is_win32_mount_point && ff_pkt->flags & FO_MULTIFS) {
710 return 1; /* Ignore this dir, it will backed up later */
711
712 } else if (is_win32_mount_point) {
713 recurse = false; /* We backup the mount point itself, but nothing more */
714
715 } else if(!(ff_pkt->flags & FO_MULTIFS)) {
716 ff_pkt->type = FT_NOFSCHG;
717 recurse = false;
718
719 } else if (!accept_fstype(ff_pkt, NULL)) {
720 ff_pkt->type = FT_INVALIDFS;
721 recurse = false;
722
723 } else {
724 ff_pkt->volhas_attrlist = volume_has_attrlist(snap_fname);
725 }
726 }
727 /* If not recursing, just backup dir and return */
728 if (!recurse) {
729 rtn_stat = handle_file(jcr, ff_pkt, top_level);
730 if (ff_pkt->linked) {
731 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
732 }
733 free(link);
734 free_dir_ff_pkt(dir_ff_pkt);
735 ff_pkt->link = ff_pkt->fname; /* reset "link" */
736 if (ff_pkt->flags & FO_KEEPATIME && !ff_pkt->snapshot_convert_fct) {
737 utime(snap_fname, &restore_times); /* snap_fname==fname */
738 }
739 return rtn_stat;
740 }
741
742 ff_pkt->link = ff_pkt->fname; /* reset "link" */
743
744 /*
745 * Descend into or "recurse" into the directory to read
746 * all the files in it.
747 */
748 errno = 0;
749 if ((directory = opendir(snap_fname)) == NULL) {
750 ff_pkt->type = FT_NOOPEN;
751 ff_pkt->ff_errno = errno;
752 rtn_stat = handle_file(jcr, ff_pkt, top_level);
753 if (ff_pkt->linked) {
754 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
755 }
756 free(link);
757 free_dir_ff_pkt(dir_ff_pkt);
758 return rtn_stat;
759 }
760
761 /* Build a canonical directory name of the file inside the snapshot,
762 * with a trailing slash in link var */
763 int slen = strlen(snap_fname);
764 int snap_len = slen + 200;
765 char *snap_link = (char *)bmalloc(snap_len + 2);
766 bstrncpy(snap_link, snap_fname, snap_len);
767 /* Strip all trailing slashes */
768 while (slen >= 1 && IsPathSeparator(snap_link[slen - 1]))
769 slen--;
770 snap_link[slen++] = '/'; /* add back one */
771 snap_link[slen] = 0;
772
773 /*
774 * Process all files in this directory entry (recursing).
775 * This would possibly run faster if we chdir to the directory
776 * before traversing it.
777 */
778 rtn_stat = 1;
779 while (!job_canceled(jcr)) {
780 char *p, *q, *s;
781 int l;
782 int i;
783
784 status = breaddir(directory, dname.addr());
785 if (status != 0) {
786 /* error or end of directory */
787 // Dmsg1(99, "breaddir returned stat=%d\n", status);
788 break;
789 }
790 p = dname.c_str();
791 /* Skip `.', `..', and excluded file names. */
792 if (p[0] == '\0' || (p[0] == '.' && (p[1] == '\0' ||
793 (p[1] == '.' && p[2] == '\0')))) {
794 continue;
795 }
796 l = strlen(dname.c_str());
797 if (l + len >= link_len) {
798 link_len = len + l + 1;
799 link = (char *)brealloc(link, link_len + 1);
800 }
801 if (l + slen >= snap_len) {
802 snap_len = slen + l + 1;
803 snap_link = (char *)brealloc(snap_link, snap_len + 1);
804 }
805
806 q = link + len;
807 s = snap_link + slen;
808 for (i=0; i < l; i++) {
809 *q++ = *s++ =*p++;
810 }
811 *q = *s = 0;
812 if (!file_is_excluded(ff_pkt, link)) {
813 rtn_stat = find_one_file(jcr, ff_pkt, handle_file, link, snap_link, our_device, false);
814 if (ff_pkt->linked) {
815 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
816 }
817 }
818
819 }
820 closedir(directory);
821 free(link);
822 free(snap_link);
823
824 /*
825 * Now that we have recursed through all the files in the
826 * directory, we "save" the directory so that after all
827 * the files are restored, this entry will serve to reset
828 * the directory modes and dates. Temp directory values
829 * were used without this record.
830 */
831 handle_file(jcr, dir_ff_pkt, top_level); /* handle directory entry */
832 if (ff_pkt->linked) {
833 ff_pkt->linked->FileIndex = dir_ff_pkt->FileIndex;
834 }
835 free_dir_ff_pkt(dir_ff_pkt);
836
837 if (ff_pkt->flags & FO_KEEPATIME && !ff_pkt->snapshot_convert_fct) {
838 utime(snap_fname, &restore_times);
839 }
840 ff_pkt->volhas_attrlist = volhas_attrlist; /* Restore value in case it changed. */
841 return rtn_stat;
842 } /* end check for directory */
843
844 /*
845 * If it is explicitly mentioned (i.e. top_level) and is
846 * a block device, we do a raw backup of it or if it is
847 * a fifo, we simply read it.
848 */
849 #ifdef HAVE_FREEBSD_OS
850 /*
851 * On FreeBSD, all block devices are character devices, so
852 * to be able to read a raw disk, we need the check for
853 * a character device.
854 * crw-r----- 1 root operator - 116, 0x00040002 Jun 9 19:32 /dev/ad0s3
855 * crw-r----- 1 root operator - 116, 0x00040002 Jun 9 19:32 /dev/rad0s3
856 */
857 if (top_level && (S_ISBLK(ff_pkt->statp.st_mode) || S_ISCHR(ff_pkt->statp.st_mode))) {
858 #else
859 if (top_level && S_ISBLK(ff_pkt->statp.st_mode)) {
860 #endif
861 ff_pkt->type = FT_RAW; /* raw partition */
862 } else if (top_level && S_ISFIFO(ff_pkt->statp.st_mode) &&
863 ff_pkt->flags & FO_READFIFO) {
864 ff_pkt->type = FT_FIFO;
865 } else {
866 /* The only remaining types are special (character, ...) files */
867 ff_pkt->type = FT_SPEC;
868 }
869 rtn_stat = handle_file(jcr, ff_pkt, top_level);
870 if (ff_pkt->linked) {
871 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
872 }
873 return rtn_stat;
874 }
875
876 int term_find_one(FF_PKT *ff)
877 {
878 struct f_link *lp, *lc;
879 int count = 0;
880 int i;
881
882
883 if (ff->linkhash == NULL) return 0;
884
885 for (i =0 ; i < LINK_HASHTABLE_SIZE; i ++) {
886 /* Free up list of hard linked files */
887 lp = ff->linkhash[i];
888 while (lp) {
889 lc = lp;
890 lp = lp->next;
891 if (lc) {
892 if (lc->digest) {
893 free(lc->digest);
894 }
895 free(lc);
896 count++;
897 }
898 }
899 ff->linkhash[i] = NULL;
900 }
901 free(ff->linkhash);
902 ff->linkhash = NULL;
903 return count;
904 }
905