]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/fd_snapshot.c
Big backport from Enterprise
[bacula/bacula] / bacula / src / filed / fd_snapshot.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 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    Written by Eric Bollengier, 2015
20 */
21
22 /*
23            Documentation about snapshot backend
24 ----------------------------------------------------------------
25
26 The snapshot manager is using environment variables to communicate.
27
28 Variables:
29
30 SNAPSHOT_ACTION
31   Operation such as:
32      create, delete, list, mount, unmount, check, support, subvolume
33
34 SNAPSHOT_VOLUME
35   Volume name
36    ex: /dev/vgroot/home_Test-2014-01-01_00_00  (lvm)
37        /home/.snapshots/Test-2014-01-01_00_00  (btrfs)
38        /.zfs/snapshot/Test-2014-01-01_00_00    (zfs)
39
40   The volume name is generated by the create command
41
42 SNAPSHOT_DEVICE
43   Device name
44    ex: /dev/vgroot/home (lvm)
45        /home            (btrfs)
46        /                (zfs)
47
48   The device name can be found via getmntent()
49
50 SNAPSHOT_NAME
51   Snapshot name, usually the Job name
52
53 SNAPSHOT_FSTYPE
54   Device filesystem type, can be found via getmntent()
55   ex: btrfs, zfs, ext4
56
57 SNAPSHOT_TYPE
58   Snapshot backend type, generated by support command
59   ex: lvm, btrfs, zfs
60
61 SNAPSHOT_MOUNTPOINT
62   Device mount point, found via getmntent()
63
64 SNAPSHOT_SNAPMOUNTPOINT
65   Snapshot mount point is generated by the mount command
66
67
68                    Protocol
69 ----------------------------------------------------------------
70
71 OK: exit code 0 and status=1 in the output
72 ERR: exit code <> 0 and/or status=0 in the output
73
74 status=1 keyword="value" keyword2="value2"
75
76 status=0 error="Error message"
77
78
79                    Workflow
80 ----------------------------------------------------------------
81
82 1) List filesystems
83  get SNAPSHOT_DEVICE, SNAPSHOT_FSTYPE, SNAPSHOT_MOUNTPOINT
84
85  volume="" name="" device="" createtime=""
86
87 2) Test if a filesystem supports snapshot feature
88  SNAPSHOT_ACTION=support
89  SNAPSHOT_DEVICE=/home
90  SNAPSHOT_MOUNTPOINT=/home
91  SNAPSHOT_FSTYPE=btrfs
92
93  => status=1 type=btrfs device=/home
94  => status=0
95
96 2a) Test if a filesystem contains subvolumes
97  SNAPSHOT_ACTION=subvolumes
98  SNAPSHOT_DEVICE=/home
99  SNAPSHOT_FSTYPE=btrfs
100
101  => dev=10 mountpoint=/home/subvol fstype=btrfs
102
103 3) Create a snapshot
104  SNAPSHOT_ACTION=create
105  SNAPSHOT_NAME=Test-2014-01-01_00_00 
106  SNAPSHOT_DEVICE=/home
107  SNAPSHOT_MOUNTPOINT=/home
108  SNAPSHOT_FSTYPE=btrfs
109  SNAPSHOT_TYPE=btrfs
110
111  => status=1 volume="/home/.snapshots/Test-2014-01-01_00_00" createtdate=1418935776 type=btrfs
112
113 4) Mount the snapshot
114  SNAPSHOT_ACTION=mount
115  SNAPSHOT_NAME=Test-2014-01-01_00_00 
116  SNAPSHOT_DEVICE=/home
117  SNAPSHOT_MOUNTPOINT=/home
118  SNAPSHOT_FSTYPE=btrfs
119  SNAPSHOT_TYPE=btrfs
120
121  => status=1 volume="/home/.snapshots/Test-2014-01-01_00_00" createtdate=1418935776 type=btrfs
122  
123
124 5) Unmount the snapshot
125  SNAPSHOT_ACTION=unmount
126  SNAPSHOT_SNAPMOUNTPOINT=/home/.snapshots/Test-2014-01-01_00_00
127
128  => status=1
129
130 6) Delete the snapshot
131  SNAPSHOT_ACTION=delete
132  SNAPSHOT_VOLUME=/home/.snapshot/Test-2014-01-01_00_00
133
134  => status=1
135
136  */
137
138 #include "bacula.h"
139 #include "filed.h"
140 #define USE_CMD_PARSER
141 #include "plugins/fd/fd_common.h"
142 #undef Jmsg
143 #include "fd_snapshot.h"
144 #define APPMANAGER_CMD "%eappmanager"
145 #define APP_DIR "/tmp/app.d"
146 #define SNAPSHOT_CMD "%ebsnapshot"
147
148 /* Defined in messages.c */
149 extern char *exepath;
150
151 /* Catalog interface with the director */
152 static char CreateSnap[] = "CatReq Job=%s new_snapshot name=%s volume=%s device=%s tdate=%d type=%s retention=%s";
153 static char DelSnap[] = "CatReq Job=%s del_snapshot name=%s device=%s";
154 static char GetSnap[] = "CatReq Job=%s get_snapshot name=%s volume=%s";
155
156 /* Command line interface with the director */
157 static char LsCmd[] = "snapshot ls name=%127s volume=%s device=%s tdate=%d type=%127s path=%s";
158 static char DelCmd[] = "snapshot del name=%127s volume=%s device=%s tdate=%d type=%127s";
159 static char QueryCmd[] = "snapshot query name=%127s volume=%s device=%s tdate=%d type=%127s";
160 static char PruneCmd[] = "snapshot prune volume=%s type=%127s";
161 static char SyncCmd[] = "snapshot sync volume=%s type=%127%";
162 static char ListCmd[] = "snapshot list";
163 static char ConfCmd[] = "snapshot retention=%50s";
164
165 /* Small function to quickly tell us if we can do snapshot here */
166 static bool is_snapshot_supported(JCR *jcr)
167 {
168    bool ret;
169    struct stat sp;
170    POOLMEM    *cmd = get_pool_memory(PM_FNAME);
171    const char *p;
172    const char *str;
173    char add[20];
174
175    /* We are not really interested by arguments, just
176     * the filename
177     */
178    *cmd = 0;
179    for (p=me->snapshot_command; *p; p++) {
180       if (*p == '%') {
181          switch (*++p) {
182          case '%':
183             str = "%";
184             break;
185          case 'e':
186             str = NPRTB(exepath);
187             break;
188          default:
189             add[0] = '%';
190             add[1] = *p;
191             add[2] = 0;
192             str = add;
193          }
194
195       } else if (*p == ' ') {
196          break;
197
198       } else {
199          add[0] = *p;
200          add[1] = 0;
201          str = add;
202       }
203       pm_strcat(cmd, str);
204    }
205
206    ret = stat(cmd, &sp) == 0;
207    free_pool_memory(cmd);
208    Dmsg1(10, "Snapshot = %d\n", ret);
209    return ret;
210 }
211
212 /* Return the default snapshot handler, must be freed at the end */
213 char *snapshot_get_command()
214 {
215    return bstrdup(SNAPSHOT_CMD);
216 }
217
218 /* Initialize the snapshot manager at the begining of the
219  * job and create snapshots
220  */
221 bool open_snapshot_backup_session(JCR *jcr)
222 {
223    if (!is_snapshot_supported(jcr)) {
224       Dmsg0(DT_SNAPSHOT, "Snapshot not supported\n");
225       return false;
226    }
227
228    jcr->snap_mgr = New(snapshot_manager(jcr));
229    /* Get all volumes and subvolumes */
230    if (!jcr->snap_mgr->scan_mtab()) {
231       berrno be;                /* error probably in errno */
232       Dmsg1(DT_SNAPSHOT, "Unable to scan mtab. ERR=%s\n", be.bstrerror());
233       Jmsg(jcr, M_ERROR, 0, "Unable to scan mtab to determine devices to snapshot\n");
234       return false;
235    }
236    /* Match the volume list with the fileset */
237    if (!jcr->snap_mgr->scan_fileset()) {
238       Jmsg(jcr, M_ERROR,0, "Unable to scan fileset to determine devices to snapshot\n");
239       return false;
240    }
241    /* Create fileset needed */
242    if (!jcr->snap_mgr->create_snapshots()) {
243       /* Error message already displayed if needed */
244       return false;
245    }
246    return true;                 /* We should have some snapshots */
247 }
248
249 /* Command that can be called from outside */
250 bool list_all_snapshots(JCR *jcr, alist *lst)
251 {
252    snapshot_manager snap_mgr(jcr);
253
254    /* Get all volumes and subvolumes */
255    if (!snap_mgr.scan_mtab()) {
256       return false;
257    }
258    /* list snapshots */
259    if (snap_mgr.list_snapshots(lst)) {
260       return false;
261    }
262    return true;
263 }
264
265 /* Cleanup the snapshot manager at the end of the job */
266 void close_snapshot_backup_session(JCR *jcr)
267 {
268    if (jcr->snap_mgr) {
269       jcr->snap_mgr->cleanup_snapshots();
270       delete jcr->snap_mgr;
271       jcr->snap_mgr = NULL;
272    }
273 }
274
275 class snapshot;
276
277 /* Device that exists on the system */
278 class fs_device: public SMARTALLOC
279 {
280 public:
281    rblink link;
282
283    uint32_t  dev;               /* dev no */
284    char     *mountpoint;        /* where it's mounted */
285    char     *fstype;            /* ntfs, ext3, ext4... */
286    char     *device;            /* /dev/mapper/xxx */
287
288    bool      supportSnapshotTested; /* True if support() was called */
289    bool      isSuitableForSnapshot; /* Compatible with snapshots */
290    bool      inSnapshotSet;
291    bool      inFileSet;
292    snapshot *snap;              /* Associated snapshot */
293
294    dlist     *include;          /* Where the fs_device was found in the fileset */
295    void      *node;             /* At which node */
296
297
298    fs_device(): 
299       dev(0), mountpoint(NULL), fstype(NULL), device(NULL), 
300          supportSnapshotTested(false), isSuitableForSnapshot(false), snap(NULL)
301       {
302       };
303
304    fs_device(uint32_t adev, const char *adevice, const char *amountpoint, const char *aftype) {
305       dev        = adev;
306       fstype     = bstrdup(aftype);
307       device    = bstrdup(adevice);
308       mountpoint = bstrdup(amountpoint);
309       supportSnapshotTested = false;
310       isSuitableForSnapshot = false;
311       inSnapshotSet = false;
312       inFileSet = false;
313       snap       = NULL;
314       include    = NULL;
315       node       = NULL;
316    };
317
318    ~fs_device() {
319       destroy();
320    };
321
322    /* Call support() and cache the result in supportSnapshotTested and isSuitableForSnapshot */
323    bool can_do_snapshot();
324
325    void setInFileSet(dlist *inc, void *where) {
326       include = inc;            /* where we need to include subvolumes */
327       node = where;             /* after which node */
328       inFileSet = true;
329       inSnapshotSet = true;
330    };
331
332    void set_snap(snapshot *s) {
333       snap = s;
334    };
335
336    void destroy();
337 };
338
339 /* The device list is stored in a rblist, using the
340  * dev no as key. The devno can be found in every stat()
341  * packet.
342  */
343 static int compare_entries(void *item1, void *item2)
344 {
345    fs_device *dev1 = (fs_device *) item1;
346    fs_device *dev2 = (fs_device *) item2;
347    if (dev1->dev > dev2->dev) {
348       return 1;
349
350    } else if (dev1->dev < dev2->dev) {
351       return -1;
352
353    } else {
354       return 0;
355    }
356 }
357
358 static int search_entry(void *item1, void *item2)
359 {
360    uint32_t dev1 = (intptr_t) item1;
361    fs_device* dev2 = (fs_device *) item2;
362    if (dev1 > dev2->dev) {
363       return 1;
364
365    } else if (dev1 < dev2->dev) {
366       return -1;
367
368    } else {
369       return 0;
370    }
371 }
372
373 /* List of all fd_device that are on the system
374  * Some devices are excluded automatically from
375  * the list, such as proc, sysfs, etc...
376  */
377 class mtab: public SMARTALLOC
378 {
379 public:
380    rblist     *entries;
381    int         sCount;          /* Snapshot count */
382    int         dCount;          /* Device count */
383    mtab() {
384       fs_device *elt = NULL;
385       entries = New(rblist(elt, &elt->link));
386       dCount = sCount = 0;
387    };
388
389    ~mtab() {
390       fs_device *elt;
391       foreach_rblist(elt, entries) {
392          elt->destroy();
393       }
394       delete entries;
395    };
396
397    /* Have we devices for snapshot in our list ? */
398    bool empty() {
399       return sCount == 0;
400    };
401
402    /* Get a fs_device corresponding to a file */
403    fs_device *search(char *file);
404
405    /* Get subvolumes for a specific device */
406    bool get_subvolumes(uint32_t dev, alist *items, FF_PKT *ff) {
407       fs_device *elt, *elt2;
408       elt = (fs_device *)entries->search((void*)(intptr_t)dev, search_entry);
409       if (!elt) {
410          return false;
411       }
412
413       foreach_rblist(elt2, entries) {
414          if (elt2->dev == elt->dev) {
415             continue;
416          }
417          if (strncmp(elt2->mountpoint, elt->mountpoint, strlen(elt->mountpoint)) == 0) {
418             /* the mount point is included in the volume */
419
420             if (file_is_excluded(ff, elt2->mountpoint)) {
421                Dmsg1(DT_SNAPSHOT|50, "Looks to be excluded %s\n", elt2->mountpoint);
422
423             } else {
424                items->append(elt2);
425             }
426          }
427       }
428       return items->size() > 0;
429    };
430
431    bool add_in_snapshot_set(char *file, dlist *inc, void *node) {
432
433       fs_device *elt = search(file);
434       if (!elt) {
435          Dmsg1(DT_SNAPSHOT, "%s will not be added to snapshot set\n", file);
436          return sCount == dCount;        /* not found in our list, skip it */
437       }
438       return add_in_snapshot_set(elt, inc, node);
439    };
440
441    bool add_in_snapshot_set(fs_device *elt, dlist *inc, void *node) {
442       Dmsg4(DT_SNAPSHOT|10, "%s in=%d can=%d tested=%d\n", elt->mountpoint, elt->inSnapshotSet, 
443             elt->isSuitableForSnapshot, elt->supportSnapshotTested);
444       if (!elt->inSnapshotSet && elt->can_do_snapshot()) {
445          Dmsg1(DT_SNAPSHOT, "Marking %s for snapshot\n", elt->mountpoint);
446          elt->setInFileSet(inc, node);
447          sCount++;
448       }
449       /* It will help to count when all devices are in the snapshot set */
450       Dmsg2(DT_SNAPSHOT|10, "sCount %d = dCount %d\n", sCount, dCount); 
451       return sCount == dCount;
452    };
453
454    bool add_entry(fs_device *vol) {
455       fs_device *ret = (fs_device *) entries->insert(vol, compare_entries);
456       if (ret == vol && vol->snap) {
457          dCount++;              /* We skip directly FS such as /proc, /sys or /dev */
458       }
459       return ret == vol;
460    };
461 };
462
463 /* Snapshot descriptor, used to communicate with the snapshot
464  * backend on the system.
465  */
466 class snapshot: public SMARTALLOC
467 {
468 private:
469    JCR *jcr;
470
471 public:
472    const char *action;             /* current action */
473    char     Name[MAX_NAME_LENGTH]; /* Name of the snapshot */
474    char     Type[MAX_NAME_LENGTH]; /* lvm, btrfs, netapp */
475    char     FSType[MAX_NAME_LENGTH];     /* btrfs, zfs, ext3 */
476    char     CreateDate[MAX_TIME_LENGTH]; /* Creation date */
477    time_t   CreateTDate;                 /* Creation date in seconds */
478    int64_t  size;               /* Size of the snapshot */
479    int      status;             /* snapshot status */
480    utime_t  Retention;          /* Snapshot retention, might come from Pool/FileSet */
481
482    POOLMEM *Volume;             /* Path of the volume */
483    POOLMEM *Device;             /* Device path */
484    POOLMEM *MountPoint;         /* Device Mount point */
485    POOLMEM *SnapMountPoint;     /* Snapshot Mount point */
486    POOLMEM *path;               /* path used in ls query */
487    POOLMEM *errmsg;             /* Error message generated by commands */
488    POOLMEM *SnapDirectory;      /* Where snapshots are stored */
489
490    char   **env;                /* Variables used to call snapshot */
491    bool     mounted;            /* True if mounted on SnapMountPoint */
492    bool     created;            /* True if the snapshot is created */
493
494    snapshot(JCR *ajcr) {
495       jcr    = ajcr;
496       env    = NULL;
497       path   = get_pool_memory(PM_FNAME);
498       errmsg = get_pool_memory(PM_MESSAGE);
499       Volume = get_pool_memory(PM_FNAME);
500       Device = get_pool_memory(PM_FNAME);
501       MountPoint     = get_pool_memory(PM_FNAME);
502       SnapMountPoint = get_pool_memory(PM_FNAME);
503       SnapDirectory  = get_pool_memory(PM_FNAME);
504       reset();
505    };
506
507    ~snapshot() {
508       free_pool_memory(path);
509       free_pool_memory(errmsg);
510       free_pool_memory(Volume);
511       free_pool_memory(Device);
512       free_pool_memory(MountPoint);
513       free_pool_memory(SnapMountPoint);
514       free_pool_memory(SnapDirectory);
515       free_env();
516    };
517
518    void reset() {
519       *SnapDirectory = *Type = *FSType = *SnapMountPoint = 0;
520       *MountPoint = *Volume = *Device = *path = *errmsg = 0;
521       action    = NULL;
522       size      = -1;
523       status    = 0;
524       mounted   = false;
525       created   = false;
526       Retention = jcr->snapshot_retention;
527    };
528
529    /* Free the env[] structure */
530    void free_env() {
531       if (env) {
532          for (int i=0; env[i] ; i++) {
533             free(env[i]);
534          }
535          free(env);
536          env = NULL;
537       }
538    };
539
540    void set_device(const char *d) {
541       pm_strcpy(Device, d);
542    };
543
544    void set_mountpoint(const char *d) {
545       pm_strcpy(MountPoint, d);
546    };
547
548    void set_name(const char *n) {
549       bstrncpy(Name, n, sizeof(Name));
550    };
551
552    void set_fstype(const char *n) {
553       bstrncpy(FSType, n, sizeof(FSType));
554    };
555
556    void set_action(const char *a) {
557       action = a;
558    };
559
560    /* Convert a real top path to a snapshot path
561     * and set proper variables inside ff_pkt
562     * to translate back all subfiles.
563     */
564    bool convert_path(FF_PKT *ff) {
565       if (!*MountPoint || !*SnapMountPoint) {
566          Dmsg2(DT_SNAPSHOT, "MountPoint=%s SnapMountPoint=%s\n", 
567                NPRT(MountPoint), NPRT(SnapMountPoint));
568          return false;
569       }
570       if (!ff->snap_top_fname) {
571          ff->snap_top_fname = get_pool_memory(PM_FNAME);
572       }
573       ff->volume_path = MountPoint;       /* /tmp */
574       ff->snapshot_path = SnapMountPoint; /* /tmp/.snapshot/Job.20140502.01.01.01 */
575       ff->top_fname_save = ff->top_fname;
576
577       int mp_first = strlen(MountPoint); /* will point to after MountPoint in top_fname */
578       int last = pm_strcpy(ff->snap_top_fname, SnapMountPoint);
579       last = MAX(last - 1, 0);
580
581       /* We need to concat path and avoid double / and no / */
582       if (ff->snap_top_fname[last] == '/') {
583          if (ff->top_fname[mp_first] == '/') {
584             ff->snap_top_fname[last] = 0; /* strip double / */
585          }
586       } else {                            /* no / at all */
587          if (ff->top_fname[mp_first] != '/') {
588             pm_strcat(ff->snap_top_fname, "/");
589          }
590       }
591
592       pm_strcat(ff->snap_top_fname, ff->top_fname + mp_first);
593       ff->top_fname = ff->snap_top_fname;
594       ff->strip_snap_path = true;
595       Dmsg1(DT_SNAPSHOT|50, "top_fname=%s\n", ff->top_fname);
596       return true;
597    };
598
599    /* Create a environment used in the child process */
600    int edit_snapshot_env() {
601       int      i   = 0;
602       POOLMEM *tmp = get_pool_memory(PM_FNAME);
603       free_env();
604
605       /* Update "10" to add more variables */
606       env = (char **) malloc(sizeof(char *) * 10);
607
608       if (*Name) {
609          Mmsg(tmp, "SNAPSHOT_NAME=%s", Name);
610          env[i++] = bstrdup(tmp);
611       }
612       if (*Volume) {
613          Mmsg(tmp, "SNAPSHOT_VOLUME=%s", Volume);
614          env[i++] = bstrdup(tmp);
615       }
616       if (*Device) {
617          Mmsg(tmp, "SNAPSHOT_DEVICE=%s", Device);
618          env[i++] = bstrdup(tmp);
619       }
620       if (*Type) {
621          Mmsg(tmp, "SNAPSHOT_TYPE=%s", Type);
622          env[i++] = bstrdup(tmp);
623       }
624       if (*FSType) {
625          Mmsg(tmp, "SNAPSHOT_FSTYPE=%s", FSType);
626          env[i++] = bstrdup(tmp);
627       }
628       if (*MountPoint) {
629          Mmsg(tmp, "SNAPSHOT_MOUNTPOINT=%s", MountPoint);
630          env[i++] = bstrdup(tmp);
631       }
632       if (*SnapDirectory) {
633          Mmsg(tmp, "SNAPSHOT_SNAPDIRECTORY=%s", SnapDirectory);
634          env[i++] = bstrdup(tmp);
635       }
636       if (*SnapMountPoint) {
637          Mmsg(tmp, "SNAPSHOT_SNAPMOUNTPOINT=%s", SnapMountPoint);
638          env[i++] = bstrdup(tmp);
639       }
640       /* When adding new entries, do not forget to add more slots to env[] */
641
642       Mmsg(tmp, "SNAPSHOT_ACTION=%s", action);
643       env[i++] = bstrdup(tmp);
644
645       env[i] = NULL;            /* last record */
646
647       if (chk_dbglvl(DT_SNAPSHOT|100)) {
648          for (i = 0; env[i] ; i++) {
649             Dmsg1(0, "%s\n", env[i]);
650          }
651       }
652
653       free_pool_memory(tmp);
654       return 1;
655    };
656
657    /* Edit the command line if needed */
658    int edit_snapshot_codes(POOLMEM **omsg, const char *imsg) {
659       const char *p;
660       const char *str;
661       char add[20];
662
663       **omsg = 0;
664       for (p=imsg; *p; p++) {
665          if (*p == '%') {
666             switch (*++p) {
667             case '%':
668                str = "%";
669                break;
670             case 'e':
671                str = NPRTB(exepath);
672                break;
673             case 'n':
674                str = Name;
675                break;
676             case 'v':
677                str = Volume;
678                break;
679             case 'd':
680                str = Device;
681                break;
682             case 'D':
683                str = SnapDirectory;
684                break;
685             case 'a':
686                str = NPRT(action);
687                break;
688             case 't':
689                str = Type;
690                break;
691             case 'f':
692                str = FSType;
693                break;
694             case 'p':
695                str = MountPoint;
696                break;
697             case 's':
698                str = SnapMountPoint;
699                break;
700             default:
701                add[0] = '%';
702                add[1] = *p;
703                add[2] = 0;
704                str = add;
705             }
706
707          } else {
708             add[0] = *p;
709             add[1] = 0;
710             str = add;
711          }
712          pm_strcat(omsg, str);
713       }
714
715       if (chk_dbglvl(DT_SNAPSHOT|10)) {
716          POOL_MEM tmp;
717          Mmsg(tmp, " -d %d -o /tmp/bsnapshot.log ", debug_level);
718          pm_strcat(omsg, tmp.c_str());
719       }
720
721       Dmsg2(DT_SNAPSHOT|30, "edit_snapshot_codes: %s -> %s\n", imsg, *omsg);
722       return 1;
723    };
724
725    /* Call the snapshot backend to know if we can snapshot the current FS */
726    int support_snapshot(fs_device *vol) {
727       arg_parser cmd;
728       status = 0;
729
730       reset();
731       set_device(vol->device);
732       set_mountpoint(vol->mountpoint);
733       set_fstype(vol->fstype);
734
735       if (!do_command("support", &cmd)) {
736          goto bail_out;
737       }
738       scan_arg(&cmd);
739
740    bail_out:
741       Dmsg2(DT_SNAPSHOT|50, "%s snapshot support status=%d\n", vol->mountpoint, status);
742       return status;
743    };
744
745    /* Scan sub volumes for a particular volume */
746    int scan_subvolumes(fs_device *vol, alist *lst) {
747       int ret = 0;
748       arg_parser cmd;
749       uint32_t   dev=0;
750       char      *mp=NULL, *fstype=NULL, *device=Device;
751
752       reset();
753       set_device(vol->device);
754       set_mountpoint(vol->mountpoint);
755       set_fstype(vol->fstype);
756
757       if (!do_command("subvolumes", &cmd)) {
758          goto bail_out;
759       }
760
761       for (int i = 0; i < cmd.argc ; i++) {
762          if (strcasecmp(cmd.argk[i], "Dev") == 0 && cmd.argv[i]) {
763             dev = str_to_int64(cmd.argv[i]);
764
765          } else if (strcasecmp(cmd.argk[i], "MountPoint") == 0 && cmd.argv[i]) {
766             mp = cmd.argv[i];
767
768          } else if (strcasecmp(cmd.argk[i], "Device") == 0 && cmd.argv[i]) {
769             device = cmd.argv[i];
770
771          } else if (strcasecmp(cmd.argk[i], "FSType") == 0 && cmd.argv[i]) {
772             fstype = cmd.argv[i];
773             if (mp && fstype && dev) {
774                fs_device *elt = New(fs_device(dev, device, mp, fstype));
775                lst->append(elt);
776                /* reset variables */
777             }
778             dev = 0;
779             mp  = fstype = NULL;
780             device = Device;
781          }
782       }
783       ret = 1;
784
785    bail_out:
786       return ret;
787    };
788
789    /* Prune current snapshots
790     *  - List snapshots available on the director, keep a list locally
791     *  - get mtab, list snapshots for all devices, or devices that are in the director list
792     */
793    int prune(BSOCK *bs) {
794       return 1;
795    };
796
797    /* List local snapshots, list director snapshots, and synchronize the two */
798    int sync(BSOCK *bs) {
799       return 1;
800    };
801
802    /* List files from a snapshot
803     *  Need to set the Volume and the Path
804     */
805    int ls(BSOCK *bs) {
806       return 1;
807    };
808
809    /* Scan common arguments */
810    int scan_arg(arg_parser *cmd) {
811       for (int i = 0; i < cmd->argc ; i++) {
812          if (strcasecmp(cmd->argk[i], "Volume") == 0 && cmd->argv[i]) {
813             pm_strcpy(Volume, cmd->argv[i]);
814
815          } else if (strcasecmp(cmd->argk[i], "CreateDate") == 0 && cmd->argv[i]) {
816             bstrncpy(CreateDate, cmd->argv[i], sizeof(CreateDate));
817             CreateTDate = str_to_utime(CreateDate);
818
819          } else if (strcasecmp(cmd->argk[i], "CreateTDate") == 0 && cmd->argv[i]) {
820             CreateTDate = str_to_int64(cmd->argv[i]);
821             bstrftimes(CreateDate, sizeof(CreateDate), CreateTDate);
822
823          } else if (strcasecmp(cmd->argk[i], "Type") == 0 && cmd->argv[i]) {
824             bstrncpy(Type, cmd->argv[i], sizeof(Type));
825
826          } else if (strcasecmp(cmd->argk[i], "SnapMountPoint") == 0 && cmd->argv[i]) {
827             pm_strcpy(SnapMountPoint, cmd->argv[i]);
828
829          } else if (strcasecmp(cmd->argk[i], "SnapDirectory") == 0 && cmd->argv[i]) {
830             pm_strcpy(SnapDirectory, cmd->argv[i]);
831
832          } else if (strcasecmp(cmd->argk[i], "status") == 0 && cmd->argv[i]) {
833             status = str_to_int64(cmd->argv[i]);
834
835          } else if (strcasecmp(cmd->argk[i], "Device") == 0 && cmd->argv[i]) {
836             pm_strcpy(Device, cmd->argv[i]);
837          }
838       }
839       return 1;
840    };
841
842    /* Create a snapshot with already given attributes
843     *  Need to set Name and Device at the minimum
844     */
845    int create() {
846       int ret = 0;
847       arg_parser cmd;
848
849       if (!*Name || !*Device) {
850          goto bail_out;
851       }
852
853       Dmsg2(DT_SNAPSHOT, "Create Snapshot of %s %s\n", Device, Name);
854
855       /* TODO: see if we handle multiple snapshots per call */
856       if (!do_command("create", &cmd)) {
857          goto bail_out;
858       }
859
860       scan_arg(&cmd);
861       created = 1;
862
863       ret = 1;
864
865    bail_out:
866       return ret;
867    };
868
869    int mount() {
870       arg_parser cmd;
871       status = 0;
872
873       if (!*Name || !*Volume || !*Device || !*Type) {
874          goto bail_out;
875       }
876
877       Dmsg1(DT_SNAPSHOT, "Doing mount of %s\n", Volume);
878
879       if (!do_command("mount", &cmd)) {
880          goto bail_out;
881       }
882
883       *SnapMountPoint = 0;
884
885       scan_arg(&cmd);
886
887       mounted = (status > 0 && *SnapMountPoint);
888
889    bail_out:
890       return status;
891    };
892
893    int unmount() {
894       arg_parser cmd;
895       status = 0;
896
897       if (!*Name || !*SnapMountPoint || !*Type || !mounted) {
898          goto bail_out;
899       }
900
901       Dmsg1(DT_SNAPSHOT, "Doing unmount of a %s\n", SnapMountPoint);
902
903       if (!do_command("unmount", &cmd)) {
904          goto bail_out;
905       }
906
907       scan_arg(&cmd);
908
909       mounted = 0;
910
911    bail_out:
912       return status;
913    };
914
915    /* Delete a snapshot with the given device name */
916    int del() {
917       int ret = 0;
918       arg_parser cmd;
919       if (!*Name || !*Volume || !created) {
920          goto bail_out;
921       }
922       ret = do_command("delete", &cmd);
923    bail_out:
924       return ret;
925    };
926
927    /* TODO: Need to read stdout as well */
928    int do_command(const char *act, arg_parser *cmd) {
929       int ret = 0, rcode;
930       POOLMEM *command = get_pool_memory(PM_FNAME);
931       POOLMEM *out = get_pool_memory(PM_FNAME);
932
933       set_action(act);
934       edit_snapshot_codes(&command, me->snapshot_command);
935       edit_snapshot_env();
936
937       Dmsg1(DT_SNAPSHOT|20, "Execute %s command\n", act);
938
939       if (*command == 0) {
940          Mmsg(errmsg, _("Error while creating command string %s.\n"), act);
941          Dmsg1(DT_SNAPSHOT, "%s", errmsg);
942          goto bail_out;
943       }
944
945       /* If the exit code is 1, we can expect to have a clear error
946        * message in the output
947        */
948       if ((rcode = run_program_full_output(command, 600, out, env)) != 0) {
949          if ((rcode & ~b_errno_exit) == 1) { /* exit 1, look the output */
950             if (cmd->parse_cmd(out) == bRC_OK) {
951                int i = cmd->find_arg_with_value("status");
952                if (i >= 0) {
953                   /* If we have a status, take it */
954                   status = str_to_int64(cmd->argv[i]);
955                } else {
956                   status = 0;
957                }
958                i = cmd->find_arg_with_value("error");
959                if (i >= 0) {
960                   pm_strcpy(errmsg, cmd->argv[i]);
961                   Dmsg1(DT_SNAPSHOT|20, "%s\n", errmsg);
962                   goto bail_out;
963                }
964             }
965          }
966          berrno be;
967          Mmsg(errmsg, _("Error while executing \"%s\" %s. %s %s\n"), 
968               act, command, out, be.bstrerror());
969          Dmsg1(DT_SNAPSHOT, "%s", errmsg);
970          goto bail_out;
971       }
972
973       /* Need to decode the output of the script
974        * TODO: some commands will have multiple lines
975        */
976       if (cmd->parse_cmd(out) != bRC_OK) {
977          Dmsg2(DT_SNAPSHOT, "snapshot command %s output error: [%s]\n", act, out);
978          Mmsg(errmsg, _("Unable to parse snapshot command output\n"));
979          goto bail_out;
980       }
981       Dmsg1(DT_SNAPSHOT|50, "ret = %s\n", out);
982       ret = 1;
983    bail_out:
984       free_pool_memory(command);
985       free_pool_memory(out);
986       return ret;
987    };
988
989    int list(alist *lst) {
990       Dmsg0(DT_SNAPSHOT, "Doing list of a snapshots of a given device\n");
991       snapshot  *snap;
992       arg_parser cmd;
993       int i, ret=0, status=1;
994       char *volume=NULL, *name=NULL, *device=NULL, *createdate=NULL, *error=NULL;
995       utime_t createtdate = 0;
996
997       /* TODO: Here we need to loop over a list */
998       if (!do_command("list", &cmd)) {
999          goto bail_out;
1000       }
1001       ret = 1;
1002
1003       /* should get
1004        * volume=xxx device=zzzz name=yyy createtdate=12121212 size=xx status=xx error=xx type=lvm
1005        */
1006       for (i = 0; i < cmd.argc ; i++) {
1007          if (strcasecmp(cmd.argk[i], "Volume") == 0) {
1008             volume = cmd.argv[i];
1009
1010          } else if (strcasecmp(cmd.argk[i], "Name") == 0) {
1011             name = cmd.argv[i];
1012
1013          } else if (strcasecmp(cmd.argk[i], "Device") == 0) {
1014             device = cmd.argv[i];
1015
1016          } else if (strcasecmp(cmd.argk[i], "Error") == 0) {
1017             error = cmd.argv[i];
1018
1019          } else if (strcasecmp(cmd.argk[i], "Status") == 0) {
1020             status = str_to_int64(cmd.argv[i]);
1021
1022          } else if (strcasecmp(cmd.argk[i], "Type") == 0) {
1023             snap = New(snapshot(jcr));
1024             pm_strcpy(snap->Volume, volume);
1025             pm_strcpy(snap->Device, NPRTB(device));
1026             bstrncpy(snap->Name, NPRTB(name), sizeof(snap->Name));
1027             bstrncpy(snap->Type, cmd.argv[i], sizeof(snap->Type));
1028             bstrncpy(snap->CreateDate, createdate, sizeof(snap->CreateDate));
1029             pm_strcpy(snap->errmsg, NPRTB(error));
1030             snap->status = status;
1031             snap->CreateTDate = createtdate;
1032             error = createdate = device = name = volume = NULL;
1033             status = 1;
1034             createtdate = 0;
1035             lst->append(snap);
1036
1037          } else if (strcasecmp(cmd.argk[i], "CreateTDate") == 0) {
1038             createtdate = str_to_int64(cmd.argv[i]);
1039
1040          } else if (strcasecmp(cmd.argk[i], "CreateDate") == 0) {
1041             createdate = cmd.argv[i];
1042             createtdate = str_to_utime(cmd.argv[i]);
1043          }
1044       }
1045    bail_out:
1046       return ret;
1047    };
1048
1049    /* Query information about snapshot */
1050    int query() {
1051       Dmsg0(0, "Doing query of a snapshot\n");
1052       arg_parser cmd;
1053       int i, ret=0;
1054
1055       if (!*Volume) {
1056          goto bail_out;
1057       }
1058
1059       if (!do_command("query", &cmd)) {
1060          goto bail_out;
1061       }
1062
1063       if ((i = cmd.find_arg_with_value("size")) >= 0) {
1064          size = str_to_int64(cmd.argv[i]);
1065       }
1066
1067       if ((i = cmd.find_arg_with_value("status")) >= 0) {
1068          status = str_to_int64(cmd.argv[i]);
1069       }
1070
1071       ret = 1;
1072
1073    bail_out:
1074       return ret;
1075    };
1076
1077    /* Quickly unbash all attributes after a sscanf */
1078    void unbash_spaces() {
1079       ::unbash_spaces(Volume);
1080       ::unbash_spaces(Device);
1081       ::unbash_spaces(path);
1082       ::unbash_spaces(Name);
1083       ::unbash_spaces(CreateDate);
1084       ::unbash_spaces(errmsg);
1085    };
1086
1087    void bash_spaces() {
1088       ::bash_spaces(Volume);
1089       ::bash_spaces(Device);
1090       ::bash_spaces(path);
1091       ::bash_spaces(Name);
1092       ::bash_spaces(CreateDate);
1093       ::bash_spaces(errmsg);
1094    };
1095
1096    /* Quicky make sure we have enough space to handle the request */
1097    void check_buffer_size(int len) {
1098       Volume = check_pool_memory_size(Volume, len);
1099       Device = check_pool_memory_size(Device, len);
1100       path   = check_pool_memory_size(path, len);
1101    };
1102
1103    /* Create Catalog entry for the current snapshot */
1104    int create_catalog_entry() {
1105       int ret = 0;
1106       char ed1[50];
1107       bash_spaces();
1108       jcr->dir_bsock->fsend(CreateSnap,
1109                             jcr->Job, Name, Volume,
1110                             Device, CreateTDate, Type, edit_uint64(Retention, ed1));
1111       if (jcr->dir_bsock->recv() < 0) {
1112          Mmsg(errmsg, _("Unable to create snapshot record. ERR=%s\n"),
1113               jcr->dir_bsock->bstrerror());
1114
1115       } else if (strncmp(jcr->dir_bsock->msg, "1000", 4) != 0) {
1116          Mmsg(errmsg, _("Unable to create snapshot record, got %s\n"),
1117               jcr->dir_bsock->msg);
1118
1119       } else {
1120          ret = 1;               /* OK */
1121       }
1122       unbash_spaces();
1123       return ret;
1124    };
1125
1126    /* Delete Catalog entry of the current snapshot */
1127    int delete_catalog_entry() {
1128       int ret = 0;
1129       bash_spaces();
1130       jcr->dir_bsock->fsend(DelSnap, jcr->Job, Name, Device);
1131
1132       if (jcr->dir_bsock->recv() < 0) {
1133          Mmsg(errmsg, _("Unable to delete snapshot record. ERR=%s\n"),
1134               jcr->dir_bsock->bstrerror());
1135
1136       } else if (strncmp(jcr->dir_bsock->msg, "1000", 4) != 0) {
1137          Mmsg(errmsg, _("Unable to delete snapshot record, got %s\n"),
1138               jcr->dir_bsock->msg);
1139
1140       } else {
1141          ret = 1;               /* OK */
1142       }
1143
1144       unbash_spaces();
1145       return ret;
1146    };
1147
1148    /* Get Catalog entry of the current snapshot */
1149    int get_catalog_entry() {
1150       int ret = 0;
1151       arg_parser cmd;
1152
1153       if (!*Name || !*Volume) {
1154          return ret;
1155       }
1156
1157       bash_spaces();
1158       jcr->dir_bsock->fsend(GetSnap, jcr->Job, Name, Volume);
1159
1160       if (jcr->dir_bsock->recv() < 0) {
1161          Mmsg(errmsg, _("Unable to get snapshot record. ERR=%s\n"),
1162               jcr->dir_bsock->bstrerror());
1163
1164       } else if (strncmp(jcr->dir_bsock->msg, "1000", 4) != 0) {
1165          Mmsg(errmsg, _("Unable to get snapshot record, got %s\n"),
1166               jcr->dir_bsock->msg);
1167
1168       } else {
1169          if (cmd.parse_cmd(jcr->dir_bsock->msg) != bRC_OK) {
1170             Mmsg(errmsg, _("Unable to parse command output\n"));
1171             scan_arg(&cmd);     /* Fill all parameters from director */
1172
1173          } else {
1174             ret = 1;               /* OK */
1175          }
1176       }
1177
1178       unbash_spaces();
1179       return ret;
1180    };
1181
1182 };
1183
1184 /* Should be after snapshot declaration */
1185 void fs_device::destroy() {
1186    if (fstype) {
1187       free(fstype);
1188       fstype = NULL;
1189    }
1190    if (mountpoint) {
1191       free(mountpoint);
1192       mountpoint = NULL;
1193    }
1194    if (device) {
1195       free(device);
1196       device = NULL;
1197    }
1198    if (snap) {
1199       delete snap;
1200       snap = NULL;
1201    }
1202 }
1203
1204 bool fs_device::can_do_snapshot() {
1205    if (snap && !supportSnapshotTested) {
1206       if (snap->support_snapshot(this)) {
1207          Dmsg2(DT_SNAPSHOT, "%s suitable for snapshot, type %s\n",
1208                mountpoint, snap->Type);
1209          isSuitableForSnapshot = true;
1210
1211       } else {
1212          Dmsg2(DT_SNAPSHOT, "%s is not suitable for snapshot, type %s\n",
1213                mountpoint, snap->Type);
1214       }
1215       supportSnapshotTested = true;
1216    }
1217    return isSuitableForSnapshot;
1218 }
1219
1220 /* Should be after the snapshot declaration */
1221 fs_device *mtab::search(char *file) {
1222    struct stat statp;
1223    if (lstat(file, &statp) != 0) {
1224       Dmsg1(DT_SNAPSHOT, "%s not found\n", file);
1225       return NULL;              /* not found */
1226    }
1227    
1228    fs_device *elt = (fs_device *)entries->search((void *)((intptr_t)(statp.st_dev)),
1229                                                  search_entry);
1230    if (!elt) {
1231       Dmsg2(DT_SNAPSHOT, "Device %d for file %s not found in our mount list\n",
1232             statp.st_dev, file);
1233       return NULL;        /* not found in our list, skip it */
1234    }
1235
1236    if (!elt->can_do_snapshot()) {
1237       Dmsg2(DT_SNAPSHOT, "Device %d for file %s not snapshotable\n",
1238             statp.st_dev, file);
1239       return NULL;
1240    }
1241    Dmsg2(DT_SNAPSHOT, "Found device %d for file %s\n", elt->dev, file);
1242    return elt;
1243 }
1244
1245 /* Application to quiesce/un-quiesce */
1246 struct app {
1247    BPIPE *fd;                   /* Communication channel */
1248    char  *name;                 /* Pointer to the script name */
1249    char   cmd[1];               /* Command line */
1250 };
1251
1252 /* In the application manager, we want to run a set
1253  * of scripts and see if applications are running or
1254  * not on our partitions.
1255  *
1256  * We can specify application in the fileset, or just
1257  * try all application that are installed.
1258  *
1259  */
1260 class app_manager: public SMARTALLOC
1261 {
1262 private:
1263    JCR     *jcr;
1264    char    *appdir;             /* Where to find application scripts */
1265    alist   *applst;             /* Application list (script list to call) */
1266    int      apptimeout;         /* Timeout when trying to quiesce application */
1267    mtab    *mount_list;         /* snapshot set */
1268
1269 public:
1270     app_manager(JCR *ajcr, mtab *m, char *dir): 
1271       jcr(ajcr),
1272       appdir(dir),
1273       applst(New(alist(10, owned_by_alist))),
1274       apptimeout(300),
1275       mount_list(m) {};
1276
1277    ~app_manager() {
1278       delete applst;
1279    };
1280
1281    /* Put in a file the list of all devices that
1282     * are in the snapshot set
1283     */
1284    bool dump_snapshotset() {
1285       return true;
1286    };
1287
1288    /* Scan application */
1289    bool scan() {
1290       POOLMEM *results;
1291       bool  ret=false;
1292       char *end, *start;
1293       struct stat sp;
1294       struct app *elt = NULL;
1295
1296       if (!appdir || !*appdir || stat(appdir, &sp) == -1) {
1297          Dmsg0(DT_SNAPSHOT, "app not configured\n");
1298          return true;
1299       }
1300
1301       /* Create a file with the list of all devices that are suitable
1302        * for snapshot
1303        */
1304       dump_snapshotset();
1305
1306       results = get_pool_memory(PM_FNAME);
1307       if (run_program_full_output((char*)APPMANAGER_CMD, apptimeout, results) != 0) {
1308          berrno be;
1309          Dmsg2(DT_SNAPSHOT, "app scan error results=%s ERR=%s\n", results, be.bstrerror());
1310          goto bail_out;
1311       }
1312
1313       ret = true;
1314       start = results;
1315
1316       /* Put each line of the output in our list */
1317       for (start = results; start && *start;) {
1318          end = strchr(start, '\n');
1319          if (end) {
1320             *end = 0;
1321             elt = (struct app *) malloc(sizeof(struct app) + strlen(start) + 1);
1322
1323             elt->fd = NULL;
1324             strcpy(elt->cmd, start);
1325             elt->name = (char *)last_path_separator(elt->cmd);
1326             if (elt->name) {
1327                elt->name++;
1328
1329             } else {
1330                elt->name = elt->cmd;
1331             }
1332  
1333             applst->append(elt);
1334             Dmsg2(10, "+ %s (%s)\n", elt->name, elt->cmd);
1335             *end = '\n';
1336             end++;
1337          }
1338          start = end;
1339       }
1340    bail_out:
1341       free_pool_memory(results);
1342       return ret;
1343    };
1344
1345    bool unquiesce() {
1346       if (applst->size() == 0) {
1347          return true;
1348       }
1349
1350       Jmsg(jcr, M_INFO, 0, _("Un-Quiescing applications\n"));
1351       return true;
1352    };
1353
1354    /* Quiesce applications */
1355    bool quiesce() {
1356       bool ret = true;
1357
1358       if (applst->size() == 0) {
1359          return true;
1360       }
1361
1362       Jmsg(jcr, M_INFO, 0, _("Quiescing applications\n"));
1363
1364       for (int i = 0 ; i < applst->size() ; i++) {
1365          struct app *elt = (struct app *) applst->get(i);
1366          elt->fd = open_bpipe(elt->cmd, 0, "rw");
1367          if (!elt->fd) {
1368             /* Unable to execute the program */
1369             continue;
1370          }
1371          /* Send some commands here */
1372       }
1373       return ret;
1374    };
1375 };
1376
1377 snapshot_manager::snapshot_manager(JCR *ajcr):
1378    jcr(ajcr), mount_list(New(mtab())) {
1379 }
1380
1381 snapshot_manager::~snapshot_manager() {
1382    delete mount_list;
1383 }
1384
1385 bool snapshot_manager::cleanup_snapshots()
1386 {
1387    fs_device *elt;
1388    foreach_rblist(elt, mount_list->entries) {
1389       if (elt->can_do_snapshot() && elt->inSnapshotSet) {
1390          snapshot *s = elt->snap;
1391          if (!s->created) {
1392             continue;
1393          }
1394          if (s->unmount()) {
1395             /* TODO: Display an error? Can check mounted status */
1396          }
1397          /* When no retention is set, we delete the snapshot
1398           * just after the backup
1399           */
1400          if (s->Retention == 0) {
1401             if (s->del()) {
1402                Jmsg(jcr, M_INFO, 0, _("   Delete Snapshot for %s\n"), elt->mountpoint);
1403
1404             } else {
1405                Jmsg(jcr, M_ERROR, 0, _("   Unable to delete snapshot of %s ERR=%s\n"),
1406                     elt->mountpoint, s->errmsg);
1407             }
1408          }
1409       }
1410    }
1411    return true;
1412 }
1413
1414 bool snapshot_manager::create_snapshots()
1415 {
1416    /* No snapshot, no quiescing */
1417    if (mount_list->empty()) {
1418       Dmsg0(DT_SNAPSHOT, "The mount list is empty, no snapshot to take\n");
1419       return false;
1420    }
1421
1422    /* First thing to do is to quiesce application */
1423    app_manager apps(jcr, mount_list, (char *)APP_DIR);
1424
1425    /* TODO: Let see if we really need to abort
1426     * the snapshot part if application 
1427     */
1428    if (!apps.scan()) {
1429       return false;
1430    }
1431
1432    if (!apps.quiesce()) {
1433       return false;
1434    }
1435
1436    fs_device *elt;
1437    foreach_rblist(elt, mount_list->entries) {
1438       if (elt->can_do_snapshot() && elt->inSnapshotSet) {
1439          snapshot *s = elt->snap;
1440          if (s->create()) {
1441             Jmsg(jcr, M_INFO, 0, _("   Create Snapshot for %s\n"), elt->mountpoint);
1442
1443             if (s->Retention > 0) {/* Create snapshot catalog entry if we need to keep them */
1444                s->create_catalog_entry();
1445             }
1446
1447          } else if (s->status == 2) { /* Use Error message */
1448             elt->isSuitableForSnapshot = false; /* Disable snapshot for this device */
1449             Jmsg(jcr, M_ERROR, 0, _("   Unable to create snapshot of %s ERR=%s\n"),
1450                  elt->mountpoint, s->errmsg);
1451
1452          } else {               /* By default, an error in the creation should be fatal */
1453             Jmsg(jcr, M_FATAL, 0, _("   Unable to create snapshot of %s ERR=%s\n"),
1454                  elt->mountpoint, s->errmsg);
1455             apps.unquiesce();
1456             return false;
1457          }
1458
1459       } else {
1460          Dmsg3(DT_SNAPSHOT|20, "No Snapshot for %s suitable=%d inset=%d\n",
1461                elt->mountpoint, elt->isSuitableForSnapshot, elt->inSnapshotSet);
1462       }
1463    }
1464
1465    /* Snapshots are ok, we need to release applications */
1466    if (!apps.unquiesce()) {
1467       return false;
1468    }
1469
1470    /* Save the include context */
1471    POOL_MEM t;
1472    findINCEXE *old = get_incexe(jcr);
1473    findINCEXE *exclude = NULL;
1474    foreach_rblist(elt, mount_list->entries) {
1475       if (elt->can_do_snapshot() && elt->inSnapshotSet) {
1476          snapshot *s = elt->snap;
1477
1478          if (!s->mount()) {
1479             Jmsg(jcr, M_ERROR, 0, "   Unable to mount snapshot %s ERR=%s\n",
1480                  elt->mountpoint, s->errmsg);
1481
1482          } else if (*s->SnapDirectory) {
1483             if (!exclude) {
1484                exclude = new_exclude(jcr);
1485                /* Set the Exclude context */
1486                set_incexe(jcr, exclude);
1487             }
1488             Mmsg(t, "%s/.snapshots", elt->snap->SnapMountPoint);
1489             Dmsg1(DT_SNAPSHOT|10, "Excluding %s\n", t.c_str());
1490             add_file_to_fileset(jcr, t.c_str(), true);
1491          }
1492       }
1493    }
1494
1495    /* Restore the current context */
1496    if (exclude) {
1497       set_incexe(jcr, old);
1498    }
1499    return true;
1500 }
1501
1502 /* TODO: We might want to use some filters here */
1503 bool snapshot_manager::list_snapshots(alist *lst)
1504 {
1505    fs_device *elt;
1506
1507    /* No device, no snapshot */
1508    if (mount_list->dCount == 0) {
1509       Dmsg0(DT_SNAPSHOT, "mount list is empty, no snapshot\n");
1510       return false;
1511    }
1512
1513    foreach_rblist(elt, mount_list->entries) {
1514       if (elt->can_do_snapshot()) {
1515          elt->snap->list(lst);
1516       }
1517    }
1518    return true;
1519 }
1520
1521 void snapshot_manager::add_mount_point(uint32_t dev, const char *device,
1522                                        const char *mountpoint, const char *fstype)
1523 {
1524    bool       check=true;
1525    alist      list(10, not_owned_by_alist);
1526    fs_device *vol = New(fs_device(dev, device, mountpoint, fstype));
1527
1528    /* These FS are not supposed to use snapshot */
1529    const char *specialmp[] = {
1530       "/proc/",
1531       "/sys/",
1532       NULL
1533    };
1534    /* These FS are not supposed to use snapshot */
1535    const char *specialfs[] = {
1536       "autofs",
1537       "binfmt_misc",
1538       "cgroup",
1539       "configfs",
1540       "debugfs",
1541       "dev",
1542       "devpts",
1543       "devtmpfs",
1544       "ecryptfs",
1545       "fuse.gvfsd-fuse",
1546       "fusectl",
1547       "fd",
1548       "hugetlbfs",
1549       "mqueue",
1550       "proc",
1551       "pstore",
1552       "rpc_pipefs",
1553       "securityfs",
1554       "selinuxfs",
1555       "sysfs",
1556       "systemd-1",
1557       "tmpfs",
1558       NULL
1559    };
1560
1561    /* We skip directly /proc, /sys */
1562    for (int i=0; specialmp[i] ; i++) {
1563       if (strncasecmp(specialmp[i], mountpoint, strlen(specialmp[i])) == 0) {
1564          check = false;
1565          break;
1566       }
1567    }
1568
1569    if (check) {
1570       for (int i=0; specialfs[i] ; i++) {
1571          if (strcasecmp(specialfs[i], fstype) == 0) {
1572             check = false;
1573             break;
1574          }
1575       }
1576    }
1577
1578    if (check) {
1579       snapshot *snap = New(snapshot(jcr));
1580       snap->set_name(jcr->Job);
1581       vol->snap = snap;
1582
1583       if (snap->scan_subvolumes(vol, &list)) {
1584          for (int i = list.size() - 1 ; i >= 0 ; i--) {
1585             fs_device *d = (fs_device *)list.remove(i);
1586             add_mount_point(d->dev, d->device, d->mountpoint, d->fstype);
1587             delete d;
1588          }
1589       }
1590    }
1591
1592    Dmsg4(DT_SNAPSHOT|20, "Adding %s dev=%d snap?=%d to the mount list (%d)\n",
1593          mountpoint, dev, check, mount_list->dCount);
1594
1595    if (!mount_list->add_entry(vol)) {
1596       Dmsg1(DT_SNAPSHOT, "%s Already exists in mount list\n", vol->mountpoint);
1597       delete vol;
1598    }
1599 }
1600
1601 /* In this handler, we need to fill a mtab structure */
1602 static void add_handler(void *user_ctx,
1603                         struct stat *st,
1604                         const char *fstype,
1605                         const char *mountpoint,
1606                         const char *mntopts,
1607                         const char *device)
1608 {
1609    Dmsg5(DT_SNAPSHOT|50, "dev=%ld device=%s mountpoint=%s fstype=%s mntopts=%s\n",
1610          st->st_dev, device, mountpoint, fstype, mntopts);
1611
1612    /* TODO: If the fstype is btrfs or zfs, the fs might contains subvolumes,
1613     * and these subvolumes may not be reported in the mntent list. In this
1614     * case, we need to call proper btrfs/zfs commands to list them.
1615     *   # btrfs subvolume list /mnt
1616     *   ID 256 gen 17 top level 5 path test
1617     *   ID 259 gen 18 top level 5 path test/titi
1618     */
1619    snapshot_manager *snapmgr = (snapshot_manager *)user_ctx;
1620    snapmgr->add_mount_point(st->st_dev, device, mountpoint, fstype);
1621 }
1622
1623 bool snapshot_manager::scan_mtab()
1624 {
1625    return read_mtab(add_handler, this);
1626 }
1627
1628
1629 /* Scan the fileset to select partitions to snapshot */
1630 bool snapshot_manager::scan_fileset()
1631 {
1632    if (!jcr->ff || !jcr->ff->fileset) {
1633       Dmsg0(DT_SNAPSHOT, "No fileset associated with JCR\n");
1634       return false;
1635    }
1636
1637    findFILESET *fileset = jcr->ff->fileset;
1638    dlistString *node;
1639    int64_t      flags = 0;
1640
1641    for (int i=0; i<fileset->include_list.size(); i++) {
1642
1643       findFOPTS  *fo;
1644       findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
1645
1646       /* look through all files */
1647       foreach_dlist(node, &incexe->name_list) {
1648          char *fname = node->c_str();
1649          if (mount_list->add_in_snapshot_set(fname, &incexe->name_list, node)) {
1650             /* When all volumes are selected, we can stop */
1651             Dmsg0(DT_SNAPSHOT, "All Volumes are marked, stopping the loop here\n");
1652             goto all_included;
1653          }
1654       }
1655
1656       foreach_alist(fo, &incexe->opts_list) {
1657          flags |= fo->flags; /* We are looking for FO_MULTIFS and recurse */
1658       }
1659    }
1660
1661 all_included:
1662    /* If we allow recursion and multifs, we need to include sub volumes by hand
1663     * in the backup list
1664     */
1665    if (flags & FO_MULTIFS) {
1666       fs_device *elt, *elt2;
1667
1668       foreach_rblist(elt, mount_list->entries) {
1669          if (!elt->inFileSet) {
1670             continue;
1671          }
1672          alist     *lst = New(alist(10, not_owned_by_alist));
1673          mount_list->get_subvolumes(elt->dev, lst, jcr->ff);
1674          foreach_alist(elt2, lst) {
1675             if (elt2->inFileSet) {
1676                continue;
1677             }
1678
1679             /* TODO: See how to avoid having two entries for the same directory */
1680             /* Add the directory explicitely in the fileset */
1681             elt->include->insert_after(new_dlistString(elt2->mountpoint), elt->node);
1682
1683             if (mount_list->add_in_snapshot_set(elt2, elt->include, elt->node)) {
1684                /* When all volumes are selected, we can stop */
1685                Dmsg0(DT_SNAPSHOT, "All Volumes are marked, stopping the loop here\n");
1686                delete lst;
1687                return true;
1688             }
1689          }
1690          delete lst;
1691       }
1692    }
1693    return true;
1694 }
1695
1696 int snapshot_cmd(JCR *jcr)
1697 {
1698    BSOCK *dir = jcr->dir_bsock;
1699    int n, ret = 1;
1700    char ed1[50];
1701    snapshot snap(jcr);
1702    snap.check_buffer_size(dir->msglen);
1703
1704    n = sscanf(dir->msg, QueryCmd, snap.Name, snap.Volume, snap.Device, &snap.CreateTDate, snap.Type);
1705    if (n == 5) {
1706       snap.unbash_spaces();
1707       Dmsg0(DT_SNAPSHOT|10, "Doing query of a snapshot\n");
1708       n = snap.query();
1709       bash_spaces(snap.errmsg);
1710       dir->fsend("%d Snapshot status=%d size=%lld ERR=%s\n", n?2000:2999, snap.status, snap.size, snap.errmsg);
1711       goto bail_out;
1712    }
1713
1714    n = sscanf(dir->msg, LsCmd, snap.Name, snap.Volume, snap.Device, &snap.CreateTDate, snap.Type, snap.path);
1715    if (n == 6) {
1716       snap.unbash_spaces();
1717       Dmsg0(DT_SNAPSHOT|10, "Doing ls of a snapshot\n");
1718       n = snap.ls(dir);
1719       dir->signal(BNET_EOD);
1720       goto bail_out;
1721    }
1722
1723    n = sscanf(dir->msg, DelCmd, snap.Name, snap.Volume, snap.Device, &snap.CreateTDate, snap.Type);
1724    if (n == 5) {
1725       Dmsg0(DT_SNAPSHOT|10, "Doing del of a snapshot\n");
1726       snap.unbash_spaces();
1727       n = snap.del();
1728       bash_spaces(snap.errmsg);
1729       dir->fsend("%d Snapshot deleted ERR=%s\n", n?2000:2999, snap.errmsg);
1730       goto bail_out;
1731    }
1732
1733    n = sscanf(dir->msg, PruneCmd, snap.Volume, snap.Type);
1734    if (n == 2) {
1735       snap.unbash_spaces();
1736       n = snap.prune(dir);
1737       bash_spaces(snap.errmsg);
1738       dir->fsend("%d Snapshot pruned ERR=%s\n", n?2000:2999, snap.errmsg);
1739       Dmsg0(DT_SNAPSHOT|10, "Doing pruning of snapshots\n");
1740       goto bail_out;
1741    }
1742
1743    n = sscanf(dir->msg, SyncCmd, snap.Volume, snap.Type);
1744    if (n == 2) {
1745       snap.unbash_spaces();
1746       n = snap.sync(dir);
1747       bash_spaces(snap.errmsg);
1748       dir->fsend("%d Snapshot synced ERR=%s\n", n?2000:2999, snap.errmsg);
1749       Dmsg0(DT_SNAPSHOT|10, "Doing sync of snapshots\n");
1750       goto bail_out;
1751    }
1752
1753    /* TODO: Include a path name or a device name */
1754    if (strncmp(dir->msg, ListCmd, strlen(ListCmd)) == 0) {
1755       snapshot *elt;
1756       char      ed1[50];
1757       alist    *lst = New(alist(10, not_owned_by_alist));
1758       list_all_snapshots(jcr, lst);
1759       foreach_alist(elt, lst) {
1760          elt->bash_spaces();
1761          dir->fsend("volume=\"%s\" createtdate=\"%s\" name=\"%s\" device=\"%s\" status=%d error=\"%s\" type=\"%s\"\n",
1762                     elt->Volume, edit_uint64(elt->CreateTDate, ed1), 
1763                     elt->Name, elt->Device, elt->status, elt->errmsg, elt->Type);
1764          delete elt;
1765       }
1766       delete lst;
1767       dir->signal(BNET_EOD);      
1768       goto bail_out;
1769    }
1770
1771    n = sscanf(dir->msg, ConfCmd, ed1);
1772    if (n == 1) {
1773       jcr->snapshot_retention = str_to_uint64(ed1);
1774       dir->fsend("2000 Snapshot retention\n");
1775       goto bail_out;
1776    }
1777
1778    dir->fsend("2999 Snapshot command not found\n");
1779    dir->signal(BNET_EOD);
1780    ret = 0;
1781
1782 bail_out:
1783    return ret;
1784 }
1785
1786 bool snapshot_convert_path(JCR *jcr, FF_PKT *ff, dlist *filelist, dlistString *node)
1787 {
1788    Dmsg1(DT_SNAPSHOT, "snapshot_convert_path(%s)\n", ff->top_fname);
1789    snapshot_manager *snapmgr = jcr->snap_mgr;
1790    ff->strip_snap_path = false;
1791
1792    if (!snapmgr) {
1793       return true;
1794    }
1795
1796    fs_device *elt = snapmgr->mount_list->search(ff->top_fname);
1797    if (!elt) {
1798       return true;              /* not found */
1799    }
1800
1801    if (!ff->snap_fname) {
1802       ff->snap_fname = get_pool_memory(PM_FNAME);
1803    }
1804
1805    /* Convert the filename to the original path */
1806    if (!elt->snap->convert_path(ff)) {
1807       Dmsg2(DT_SNAPSHOT, "Device %d for file %s not snapshotable\n",
1808             elt->dev, ff->top_fname);
1809       return true;
1810    }
1811    return true;
1812 }
1813
1814 /* ListSnap[] = "CatReq Job=%s list_snapshot name=%s volume=%s device=%s tdate=%d type=%s before=%s after=%s expired=%d"; */
1815
1816 /* List Catalog entry of the current client */
1817 int snapshot_list_catalog(JCR *jcr,
1818                           const char *query,
1819                           alist *lst)
1820 {
1821    int ret = 0, i;
1822    arg_parser cmd;
1823    POOL_MEM q, tmp;
1824    if (cmd.parse_cmd(query) != bRC_OK) {
1825       Dmsg1(DT_SNAPSHOT, "Unable to decode query %s\n", query);
1826       return 0;
1827    }
1828    Mmsg(q, "CatReq Job=%s list_snapshot name=", jcr->Job);
1829    if ((i = cmd.find_arg_with_value("name")) >= 0) {
1830       bash_spaces(cmd.argv[i]);
1831       pm_strcat(q, cmd.argv[i]);
1832    }
1833
1834    pm_strcat(q, " volume=");
1835    if ((i = cmd.find_arg_with_value("volume")) >= 0) {
1836       bash_spaces(cmd.argv[i]);
1837       pm_strcat(q, cmd.argv[i]);
1838    }
1839
1840    pm_strcat(q, " device=");
1841    if ((i = cmd.find_arg_with_value("device")) >= 0) {
1842       bash_spaces(cmd.argv[i]);
1843       pm_strcat(q, cmd.argv[i]);
1844    }
1845
1846    pm_strcat(q, " tdate=");
1847    if ((i = cmd.find_arg_with_value("tdate")) >= 0) {
1848       bash_spaces(cmd.argv[i]);
1849       pm_strcat(q, cmd.argv[i]);
1850    }
1851
1852    pm_strcat(q, " type=");
1853    if ((i = cmd.find_arg_with_value("type")) >= 0) {
1854       bash_spaces(cmd.argv[i]);
1855       pm_strcat(q, cmd.argv[i]);
1856    }
1857
1858    pm_strcat(q, " before=");
1859    if ((i = cmd.find_arg_with_value("before")) >= 0) {
1860       bash_spaces(cmd.argv[i]);
1861       pm_strcat(q, cmd.argv[i]);
1862    }
1863
1864    pm_strcat(q, " after=");
1865    if ((i = cmd.find_arg_with_value("after")) >= 0) {
1866       bash_spaces(cmd.argv[i]);
1867       pm_strcat(q, cmd.argv[i]);
1868    }
1869
1870    pm_strcat(q, " expired=");
1871    if ((i = cmd.find_arg_with_value("expired")) >= 0) {
1872       bash_spaces(cmd.argv[i]);
1873       pm_strcat(q, cmd.argv[i]);
1874    }
1875
1876    jcr->dir_bsock->fsend("%s\n", q.c_str());
1877
1878    while (jcr->dir_bsock->recv() > 0) {
1879       if (cmd.parse_cmd(jcr->dir_bsock->msg) != bRC_OK) {
1880          Dmsg1(DT_SNAPSHOT, "Unable to decode director output %s\n", jcr->dir_bsock->msg);
1881
1882       } else {
1883          ret = 1;               /* OK */
1884          snapshot *s = New(snapshot(jcr));
1885          s->scan_arg(&cmd);
1886          lst->append(s);
1887       }
1888    }
1889    return ret;
1890 }