12654012fSReza Sabdar /*
28c4f9701SJanice Chang  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
3*9adfa60dSMatthew Ahrens  * Copyright (c) 2013, 2015 by Delphix. All rights reserved.
4a7a845e4SSteven Hartland  * Copyright (c) 2013 Steven Hartland. All rights reserved.
50d8fa8f8SMartin Matuska  * Copyright (c) 2016 Martin Matuska. All rights reserved.
62654012fSReza Sabdar  */
72654012fSReza Sabdar 
82654012fSReza Sabdar /*
92654012fSReza Sabdar  * BSD 3 Clause License
102654012fSReza Sabdar  *
112654012fSReza Sabdar  * Copyright (c) 2007, The Storage Networking Industry Association.
122654012fSReza Sabdar  *
132654012fSReza Sabdar  * Redistribution and use in source and binary forms, with or without
142654012fSReza Sabdar  * modification, are permitted provided that the following conditions
152654012fSReza Sabdar  * are met:
162654012fSReza Sabdar  * 	- Redistributions of source code must retain the above copyright
172654012fSReza Sabdar  *	  notice, this list of conditions and the following disclaimer.
182654012fSReza Sabdar  *
192654012fSReza Sabdar  * 	- Redistributions in binary form must reproduce the above copyright
202654012fSReza Sabdar  *	  notice, this list of conditions and the following disclaimer in
212654012fSReza Sabdar  *	  the documentation and/or other materials provided with the
222654012fSReza Sabdar  *	  distribution.
232654012fSReza Sabdar  *
242654012fSReza Sabdar  *	- Neither the name of The Storage Networking Industry Association (SNIA)
252654012fSReza Sabdar  *	  nor the names of its contributors may be used to endorse or promote
262654012fSReza Sabdar  *	  products derived from this software without specific prior written
272654012fSReza Sabdar  *	  permission.
282654012fSReza Sabdar  *
292654012fSReza Sabdar  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
302654012fSReza Sabdar  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
312654012fSReza Sabdar  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
322654012fSReza Sabdar  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
332654012fSReza Sabdar  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
342654012fSReza Sabdar  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
352654012fSReza Sabdar  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
362654012fSReza Sabdar  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
372654012fSReza Sabdar  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
382654012fSReza Sabdar  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
392654012fSReza Sabdar  * POSSIBILITY OF SUCH DAMAGE.
402654012fSReza Sabdar  */
412654012fSReza Sabdar 
422654012fSReza Sabdar #include <stdio.h>
432654012fSReza Sabdar #include <string.h>
442654012fSReza Sabdar #include "ndmpd.h"
452654012fSReza Sabdar #include <libzfs.h>
462654012fSReza Sabdar 
47876b86efSReza Sabdar typedef struct snap_param {
48876b86efSReza Sabdar 	char *snp_name;
49876b86efSReza Sabdar 	boolean_t snp_found;
50876b86efSReza Sabdar } snap_param_t;
512654012fSReza Sabdar 
52876b86efSReza Sabdar static int cleanup_fd = -1;
532654012fSReza Sabdar 
542654012fSReza Sabdar /*
552654012fSReza Sabdar  * ndmp_has_backup
562654012fSReza Sabdar  *
572654012fSReza Sabdar  * Call backup function which looks for backup snapshot.
582654012fSReza Sabdar  * This is a callback function used with zfs_iter_snapshots.
592654012fSReza Sabdar  *
602654012fSReza Sabdar  * Parameters:
612654012fSReza Sabdar  *   zhp (input) - ZFS handle pointer
622654012fSReza Sabdar  *   data (output) - 0 - no backup snapshot
632654012fSReza Sabdar  *		     1 - has backup snapshot
642654012fSReza Sabdar  *
652654012fSReza Sabdar  * Returns:
662654012fSReza Sabdar  *   0: on success
672654012fSReza Sabdar  *  -1: otherwise
682654012fSReza Sabdar  */
692654012fSReza Sabdar static int
ndmp_has_backup(zfs_handle_t * zhp,void * data)702654012fSReza Sabdar ndmp_has_backup(zfs_handle_t *zhp, void *data)
712654012fSReza Sabdar {
722654012fSReza Sabdar 	const char *name;
73876b86efSReza Sabdar 	snap_param_t *chp = (snap_param_t *)data;
742654012fSReza Sabdar 
752654012fSReza Sabdar 	name = zfs_get_name(zhp);
762654012fSReza Sabdar 	if (name == NULL ||
77876b86efSReza Sabdar 	    strstr(name, chp->snp_name) == NULL) {
782654012fSReza Sabdar 		zfs_close(zhp);
792654012fSReza Sabdar 		return (-1);
802654012fSReza Sabdar 	}
812654012fSReza Sabdar 
82876b86efSReza Sabdar 	chp->snp_found = 1;
832654012fSReza Sabdar 	zfs_close(zhp);
842654012fSReza Sabdar 
852654012fSReza Sabdar 	return (0);
862654012fSReza Sabdar }
872654012fSReza Sabdar 
882654012fSReza Sabdar /*
89876b86efSReza Sabdar  * ndmp_has_backup_snapshot
902654012fSReza Sabdar  *
912654012fSReza Sabdar  * Returns TRUE if the volume has an active backup snapshot, otherwise,
922654012fSReza Sabdar  * returns FALSE.
932654012fSReza Sabdar  *
942654012fSReza Sabdar  * Parameters:
952654012fSReza Sabdar  *   volname (input) - name of the volume
962654012fSReza Sabdar  *
972654012fSReza Sabdar  * Returns:
982654012fSReza Sabdar  *   0: on success
992654012fSReza Sabdar  *  -1: otherwise
1002654012fSReza Sabdar  */
1012654012fSReza Sabdar static int
ndmp_has_backup_snapshot(char * volname,char * jobname)102876b86efSReza Sabdar ndmp_has_backup_snapshot(char *volname, char *jobname)
1032654012fSReza Sabdar {
1042654012fSReza Sabdar 	zfs_handle_t *zhp;
105876b86efSReza Sabdar 	snap_param_t snp;
106*9adfa60dSMatthew Ahrens 	char chname[ZFS_MAX_DATASET_NAME_LEN];
1072654012fSReza Sabdar 
1082654012fSReza Sabdar 	(void) mutex_lock(&zlib_mtx);
1092654012fSReza Sabdar 	if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) {
110876b86efSReza Sabdar 		NDMP_LOG(LOG_ERR, "Cannot open snapshot %s.", volname);
1112654012fSReza Sabdar 		(void) mutex_unlock(&zlib_mtx);
1122654012fSReza Sabdar 		return (-1);
1132654012fSReza Sabdar 	}
1142654012fSReza Sabdar 
115876b86efSReza Sabdar 	snp.snp_found = 0;
116*9adfa60dSMatthew Ahrens 	(void) snprintf(chname, ZFS_MAX_DATASET_NAME_LEN, "@%s", jobname);
117876b86efSReza Sabdar 	snp.snp_name = chname;
1182654012fSReza Sabdar 
1190d8fa8f8SMartin Matuska 	(void) zfs_iter_snapshots(zhp, B_FALSE, ndmp_has_backup, &snp);
1202654012fSReza Sabdar 	zfs_close(zhp);
1212654012fSReza Sabdar 	(void) mutex_unlock(&zlib_mtx);
1222654012fSReza Sabdar 
123876b86efSReza Sabdar 	return (snp.snp_found);
1242654012fSReza Sabdar }
1252654012fSReza Sabdar 
1262654012fSReza Sabdar /*
127876b86efSReza Sabdar  * ndmp_create_snapshot
1282654012fSReza Sabdar  *
129876b86efSReza Sabdar  * This function will parse the path to get the real volume name.
130876b86efSReza Sabdar  * It will then create a snapshot based on volume and job name.
131876b86efSReza Sabdar  * This function should be called before the NDMP backup is started.
1322654012fSReza Sabdar  *
1332654012fSReza Sabdar  * Parameters:
1342654012fSReza Sabdar  *   vol_name (input) - name of the volume
1352654012fSReza Sabdar  *
1362654012fSReza Sabdar  * Returns:
137876b86efSReza Sabdar  *   0: on success
138876b86efSReza Sabdar  *   -1: otherwise
1392654012fSReza Sabdar  */
140876b86efSReza Sabdar int
ndmp_create_snapshot(char * vol_name,char * jname)141876b86efSReza Sabdar ndmp_create_snapshot(char *vol_name, char *jname)
1422654012fSReza Sabdar {
143*9adfa60dSMatthew Ahrens 	char vol[ZFS_MAX_DATASET_NAME_LEN];
1442654012fSReza Sabdar 
145876b86efSReza Sabdar 	if (vol_name == 0 ||
146876b86efSReza Sabdar 	    get_zfsvolname(vol, sizeof (vol), vol_name) == -1)
1472654012fSReza Sabdar 		return (0);
1482654012fSReza Sabdar 
149876b86efSReza Sabdar 	/*
150876b86efSReza Sabdar 	 * If there is an old snapshot left from the previous
151876b86efSReza Sabdar 	 * backup it could be stale one and it must be
152876b86efSReza Sabdar 	 * removed before using it.
153876b86efSReza Sabdar 	 */
154876b86efSReza Sabdar 	if (ndmp_has_backup_snapshot(vol, jname))
155876b86efSReza Sabdar 		(void) snapshot_destroy(vol, jname, B_FALSE, B_TRUE, NULL);
1562654012fSReza Sabdar 
157876b86efSReza Sabdar 	return (snapshot_create(vol, jname, B_FALSE, B_TRUE));
1582654012fSReza Sabdar }
1592654012fSReza Sabdar 
1602654012fSReza Sabdar /*
161876b86efSReza Sabdar  * ndmp_remove_snapshot
1622654012fSReza Sabdar  *
163876b86efSReza Sabdar  * This function will parse the path to get the real volume name.
164876b86efSReza Sabdar  * It will then remove the snapshot for that volume and job name.
165876b86efSReza Sabdar  * This function should be called after NDMP backup is finished.
1662654012fSReza Sabdar  *
1672654012fSReza Sabdar  * Parameters:
1682654012fSReza Sabdar  *   vol_name (input) - name of the volume
1692654012fSReza Sabdar  *
1702654012fSReza Sabdar  * Returns:
171876b86efSReza Sabdar  *   0: on success
172876b86efSReza Sabdar  *   -1: otherwise
1732654012fSReza Sabdar  */
174876b86efSReza Sabdar int
ndmp_remove_snapshot(char * vol_name,char * jname)175876b86efSReza Sabdar ndmp_remove_snapshot(char *vol_name, char *jname)
1762654012fSReza Sabdar {
177*9adfa60dSMatthew Ahrens 	char vol[ZFS_MAX_DATASET_NAME_LEN];
178876b86efSReza Sabdar 
179876b86efSReza Sabdar 	if (vol_name == 0 ||
180876b86efSReza Sabdar 	    get_zfsvolname(vol, sizeof (vol), vol_name) == -1)
181876b86efSReza Sabdar 		return (0);
182876b86efSReza Sabdar 
183876b86efSReza Sabdar 	return (snapshot_destroy(vol, jname, B_FALSE, B_TRUE, NULL));
184876b86efSReza Sabdar }
185876b86efSReza Sabdar 
186876b86efSReza Sabdar /*
187876b86efSReza Sabdar  * Put a hold on snapshot
188876b86efSReza Sabdar  */
189876b86efSReza Sabdar int
snapshot_hold(char * volname,char * snapname,char * jname,boolean_t recursive)190876b86efSReza Sabdar snapshot_hold(char *volname, char *snapname, char *jname, boolean_t recursive)
191876b86efSReza Sabdar {
192876b86efSReza Sabdar 	zfs_handle_t *zhp;
193876b86efSReza Sabdar 	char *p;
194876b86efSReza Sabdar 
195876b86efSReza Sabdar 	if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) {
196876b86efSReza Sabdar 		NDMP_LOG(LOG_ERR, "Cannot open volume %s.", volname);
197876b86efSReza Sabdar 		return (-1);
1982654012fSReza Sabdar 	}
1992654012fSReza Sabdar 
200876b86efSReza Sabdar 	if (cleanup_fd == -1 && (cleanup_fd = open(ZFS_DEV,
201876b86efSReza Sabdar 	    O_RDWR|O_EXCL)) < 0) {
202876b86efSReza Sabdar 		NDMP_LOG(LOG_ERR, "Cannot open dev %d", errno);
203876b86efSReza Sabdar 		zfs_close(zhp);
204876b86efSReza Sabdar 		return (-1);
205876b86efSReza Sabdar 	}
206876b86efSReza Sabdar 
207876b86efSReza Sabdar 	p = strchr(snapname, '@') + 1;
208a7a845e4SSteven Hartland 	if (zfs_hold(zhp, p, jname, recursive, cleanup_fd) != 0) {
209876b86efSReza Sabdar 		NDMP_LOG(LOG_ERR, "Cannot hold snapshot %s", p);
210876b86efSReza Sabdar 		zfs_close(zhp);
211876b86efSReza Sabdar 		return (-1);
212876b86efSReza Sabdar 	}
213876b86efSReza Sabdar 	zfs_close(zhp);
2142654012fSReza Sabdar 	return (0);
2152654012fSReza Sabdar }
2162654012fSReza Sabdar 
217876b86efSReza Sabdar int
snapshot_release(char * volname,char * snapname,char * jname,boolean_t recursive)218876b86efSReza Sabdar snapshot_release(char *volname, char *snapname, char *jname,
219876b86efSReza Sabdar     boolean_t recursive)
220876b86efSReza Sabdar {
221876b86efSReza Sabdar 	zfs_handle_t *zhp;
222876b86efSReza Sabdar 	char *p;
223876b86efSReza Sabdar 	int rv = 0;
2242654012fSReza Sabdar 
225876b86efSReza Sabdar 	if ((zhp = zfs_open(zlibh, volname, ZFS_TYPE_DATASET)) == 0) {
226876b86efSReza Sabdar 		NDMP_LOG(LOG_ERR, "Cannot open volume %s", volname);
227876b86efSReza Sabdar 		return (-1);
228876b86efSReza Sabdar 	}
2292654012fSReza Sabdar 
230876b86efSReza Sabdar 	p = strchr(snapname, '@') + 1;
231876b86efSReza Sabdar 	if (zfs_release(zhp, p, jname, recursive) != 0) {
232876b86efSReza Sabdar 		NDMP_LOG(LOG_DEBUG, "Cannot release snapshot %s", p);
233876b86efSReza Sabdar 		rv = -1;
234876b86efSReza Sabdar 	}
235876b86efSReza Sabdar 	if (cleanup_fd != -1) {
236876b86efSReza Sabdar 		(void) close(cleanup_fd);
237876b86efSReza Sabdar 		cleanup_fd = -1;
238876b86efSReza Sabdar 	}
239876b86efSReza Sabdar 	zfs_close(zhp);
240876b86efSReza Sabdar 	return (rv);
241876b86efSReza Sabdar }
2422654012fSReza Sabdar 
2432654012fSReza Sabdar /*
244876b86efSReza Sabdar  * Create a snapshot on the volume
2452654012fSReza Sabdar  */
2462654012fSReza Sabdar int
snapshot_create(char * volname,char * jname,boolean_t recursive,boolean_t hold)247876b86efSReza Sabdar snapshot_create(char *volname, char *jname, boolean_t recursive,
248876b86efSReza Sabdar     boolean_t hold)
2492654012fSReza Sabdar {
250*9adfa60dSMatthew Ahrens 	char snapname[ZFS_MAX_DATASET_NAME_LEN];
251876b86efSReza Sabdar 	int rv;
2522654012fSReza Sabdar 
253876b86efSReza Sabdar 	if (!volname || !*volname)
254876b86efSReza Sabdar 		return (-1);
2552654012fSReza Sabdar 
256*9adfa60dSMatthew Ahrens 	(void) snprintf(snapname, ZFS_MAX_DATASET_NAME_LEN,
257*9adfa60dSMatthew Ahrens 	    "%s@%s", volname, jname);
258876b86efSReza Sabdar 
259876b86efSReza Sabdar 	(void) mutex_lock(&zlib_mtx);
260876b86efSReza Sabdar 	if ((rv = zfs_snapshot(zlibh, snapname, recursive, NULL))
261876b86efSReza Sabdar 	    == -1) {
262876b86efSReza Sabdar 		if (errno == EEXIST) {
263876b86efSReza Sabdar 			(void) mutex_unlock(&zlib_mtx);
264876b86efSReza Sabdar 			return (0);
265876b86efSReza Sabdar 		}
266876b86efSReza Sabdar 		NDMP_LOG(LOG_DEBUG,
267876b86efSReza Sabdar 		    "snapshot_create: %s failed (err=%d): %s",
268876b86efSReza Sabdar 		    snapname, errno, libzfs_error_description(zlibh));
269876b86efSReza Sabdar 		(void) mutex_unlock(&zlib_mtx);
270876b86efSReza Sabdar 		return (rv);
271876b86efSReza Sabdar 	}
272876b86efSReza Sabdar 	if (hold && snapshot_hold(volname, snapname, jname, recursive) != 0) {
273876b86efSReza Sabdar 		NDMP_LOG(LOG_DEBUG,
274876b86efSReza Sabdar 		    "snapshot_create: %s hold failed (err=%d): %s",
275876b86efSReza Sabdar 		    snapname, errno, libzfs_error_description(zlibh));
276876b86efSReza Sabdar 		(void) mutex_unlock(&zlib_mtx);
277876b86efSReza Sabdar 		return (-1);
2782654012fSReza Sabdar 	}
2792654012fSReza Sabdar 
280876b86efSReza Sabdar 	(void) mutex_unlock(&zlib_mtx);
281876b86efSReza Sabdar 	return (0);
2822654012fSReza Sabdar }
2832654012fSReza Sabdar 
2842654012fSReza Sabdar /*
285876b86efSReza Sabdar  * Remove and release the backup snapshot
2862654012fSReza Sabdar  */
2872654012fSReza Sabdar int
snapshot_destroy(char * volname,char * jname,boolean_t recursive,boolean_t hold,int * zfs_err)288876b86efSReza Sabdar snapshot_destroy(char *volname, char *jname, boolean_t recursive,
289876b86efSReza Sabdar     boolean_t hold, int *zfs_err)
2902654012fSReza Sabdar {
291*9adfa60dSMatthew Ahrens 	char snapname[ZFS_MAX_DATASET_NAME_LEN];
292876b86efSReza Sabdar 	zfs_handle_t *zhp;
293876b86efSReza Sabdar 	zfs_type_t ztype;
294588541fbSReza Sabdar 	char *namep;
295876b86efSReza Sabdar 	int err;
2962654012fSReza Sabdar 
297876b86efSReza Sabdar 	if (zfs_err)
298876b86efSReza Sabdar 		*zfs_err = 0;
299876b86efSReza Sabdar 
300876b86efSReza Sabdar 	if (!volname || !*volname)
301876b86efSReza Sabdar 		return (-1);
302876b86efSReza Sabdar 
303876b86efSReza Sabdar 	if (recursive) {
304876b86efSReza Sabdar 		ztype = ZFS_TYPE_VOLUME | ZFS_TYPE_FILESYSTEM;
305588541fbSReza Sabdar 		namep = volname;
306876b86efSReza Sabdar 	} else {
307*9adfa60dSMatthew Ahrens 		(void) snprintf(snapname, ZFS_MAX_DATASET_NAME_LEN,
308*9adfa60dSMatthew Ahrens 		    "%s@%s", volname, jname);
309588541fbSReza Sabdar 		namep = snapname;
310876b86efSReza Sabdar 		ztype = ZFS_TYPE_SNAPSHOT;
311876b86efSReza Sabdar 	}
312876b86efSReza Sabdar 
313876b86efSReza Sabdar 	(void) mutex_lock(&zlib_mtx);
314876b86efSReza Sabdar 	if (hold &&
315588541fbSReza Sabdar 	    snapshot_release(volname, namep, jname, recursive) != 0) {
316876b86efSReza Sabdar 		NDMP_LOG(LOG_DEBUG,
317876b86efSReza Sabdar 		    "snapshot_destroy: %s release failed (err=%d): %s",
318588541fbSReza Sabdar 		    namep, errno, libzfs_error_description(zlibh));
319876b86efSReza Sabdar 		(void) mutex_unlock(&zlib_mtx);
320876b86efSReza Sabdar 		return (-1);
321876b86efSReza Sabdar 	}
322876b86efSReza Sabdar 
323588541fbSReza Sabdar 	if ((zhp = zfs_open(zlibh, namep, ztype)) == NULL) {
324876b86efSReza Sabdar 		NDMP_LOG(LOG_DEBUG, "snapshot_destroy: open %s failed",
325588541fbSReza Sabdar 		    namep);
326876b86efSReza Sabdar 		(void) mutex_unlock(&zlib_mtx);
327876b86efSReza Sabdar 		return (-1);
328876b86efSReza Sabdar 	}
329876b86efSReza Sabdar 
330876b86efSReza Sabdar 	if (recursive) {
331876b86efSReza Sabdar 		err = zfs_destroy_snaps(zhp, jname, B_TRUE);
332876b86efSReza Sabdar 	} else {
333876b86efSReza Sabdar 		err = zfs_destroy(zhp, B_TRUE);
334876b86efSReza Sabdar 	}
335876b86efSReza Sabdar 
336876b86efSReza Sabdar 	if (err) {
337876b86efSReza Sabdar 		NDMP_LOG(LOG_ERR, "%s (recursive destroy: %d): %d; %s; %s",
338588541fbSReza Sabdar 		    namep,
339876b86efSReza Sabdar 		    recursive,
340876b86efSReza Sabdar 		    libzfs_errno(zlibh),
341876b86efSReza Sabdar 		    libzfs_error_action(zlibh),
342876b86efSReza Sabdar 		    libzfs_error_description(zlibh));
3432654012fSReza Sabdar 
344876b86efSReza Sabdar 		if (zfs_err)
345876b86efSReza Sabdar 			*zfs_err = err;
346876b86efSReza Sabdar 	}
3472654012fSReza Sabdar 
348876b86efSReza Sabdar 	zfs_close(zhp);
349876b86efSReza Sabdar 	(void) mutex_unlock(&zlib_mtx);
350876b86efSReza Sabdar 
351876b86efSReza Sabdar 	return (0);
3522654012fSReza Sabdar }
353